From e472269a4ea22df81e74c8cc42617d829092fd81 Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:59:51 +0800 Subject: [PATCH] feat: add agent debug cmd for cpu/mem analysis (#9181) --- agent/cmd/server/cmd/debug.go | 169 ++++++++++++++++++++++++++++++++ agent/cmd/server/cmd/root.go | 14 +++ agent/cmd/server/main.go | 11 ++- frontend/src/global/mimetype.ts | 2 +- 4 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 agent/cmd/server/cmd/debug.go create mode 100644 agent/cmd/server/cmd/root.go diff --git a/agent/cmd/server/cmd/debug.go b/agent/cmd/server/cmd/debug.go new file mode 100644 index 000000000..1ca8e9716 --- /dev/null +++ b/agent/cmd/server/cmd/debug.go @@ -0,0 +1,169 @@ +package cmd + +import ( + "fmt" + "os" + "runtime" + "runtime/pprof" + "time" + + "github.com/spf13/cobra" +) + +var ( + cpuProfile string + memProfile string + duration time.Duration + showMemStats bool + showGoroutine bool +) + +func init() { + RootCmd.AddCommand(debugCmd) + + debugCmd.Flags().StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to file") + debugCmd.Flags().StringVar(&memProfile, "memprofile", "", "write memory profile to file") + debugCmd.Flags().DurationVar(&duration, "duration", 30*time.Second, "duration for cpu profiling") + debugCmd.Flags().BoolVar(&showMemStats, "memstats", false, "show memory statistics") + debugCmd.Flags().BoolVar(&showGoroutine, "goroutine", false, "show goroutine profile") +} + +var debugCmd = &cobra.Command{ + Use: "debug", + Short: "Performance debugging with pprof", + Long: `Debug command provides CPU and memory profiling capabilities using pprof. + +Examples: + # CPU profiling for 30 seconds + 1panel-agent debug --cpuprofile=cpu.prof --duration=30s + + # Memory profiling + 1panel-agent debug --memprofile=mem.prof + + # Show memory statistics + 1panel-agent debug --memstats + + # Show goroutine information + 1panel-agent debug --goroutine + + # Combined profiling + 1panel-agent debug --cpuprofile=cpu.prof --memprofile=mem.prof --memstats`, + RunE: func(cmd *cobra.Command, args []string) error { + if cpuProfile != "" { + if err := startCPUProfile(cpuProfile); err != nil { + return fmt.Errorf("failed to start CPU profile: %v", err) + } + defer stopCPUProfile() + time.Sleep(duration) + fmt.Printf("CPU profile saved to: %s\n", cpuProfile) + } + + if memProfile != "" { + if err := writeMemProfile(memProfile); err != nil { + return fmt.Errorf("failed to write memory profile: %v", err) + } + fmt.Printf("Memory profile saved to: %s\n", memProfile) + } + + if showMemStats { + printMemStats() + } + + if showGoroutine { + printGoroutineInfo() + } + + if cpuProfile == "" && memProfile == "" && !showMemStats && !showGoroutine { + printBasicInfo() + } + return nil + }, +} + +func startCPUProfile(filename string) error { + f, err := os.Create(filename) + if err != nil { + return err + } + + if err := pprof.StartCPUProfile(f); err != nil { + f.Close() + return err + } + + return nil +} + +func stopCPUProfile() { + pprof.StopCPUProfile() +} + +func writeMemProfile(filename string) error { + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + runtime.GC() + + if err := pprof.WriteHeapProfile(f); err != nil { + return err + } + + return nil +} + +func printMemStats() { + var m runtime.MemStats + runtime.ReadMemStats(&m) + + fmt.Println("\n=== Memory Statistics ===") + fmt.Printf("Allocated memory: %d KB (%d MB)\n", m.Alloc/1024, m.Alloc/1024/1024) + fmt.Printf("Total allocated: %d KB (%d MB)\n", m.TotalAlloc/1024, m.TotalAlloc/1024/1024) + fmt.Printf("System memory: %d KB (%d MB)\n", m.Sys/1024, m.Sys/1024/1024) + fmt.Printf("Heap allocated: %d KB (%d MB)\n", m.HeapAlloc/1024, m.HeapAlloc/1024/1024) + fmt.Printf("Heap system: %d KB (%d MB)\n", m.HeapSys/1024, m.HeapSys/1024/1024) + fmt.Printf("Heap idle: %d KB (%d MB)\n", m.HeapIdle/1024, m.HeapIdle/1024/1024) + fmt.Printf("Heap in use: %d KB (%d MB)\n", m.HeapInuse/1024, m.HeapInuse/1024/1024) + fmt.Printf("Heap released: %d KB (%d MB)\n", m.HeapReleased/1024, m.HeapReleased/1024/1024) + fmt.Printf("Heap objects: %d\n", m.HeapObjects) + fmt.Printf("GC runs: %d\n", m.NumGC) + fmt.Printf("GC pause total: %d ns\n", m.PauseTotalNs) + fmt.Printf("Next GC: %d KB (%d MB)\n", m.NextGC/1024, m.NextGC/1024/1024) + fmt.Printf("Last GC: %s\n", time.Unix(0, int64(m.LastGC)).Format("2006-01-02 15:04:05")) +} + +func printGoroutineInfo() { + fmt.Println("\n=== Goroutine Information ===") + fmt.Printf("Number of goroutines: %d\n", runtime.NumGoroutine()) + fmt.Printf("Number of CPUs: %d\n", runtime.NumCPU()) + fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0)) + + buf := make([]byte, 1024*1024) + stackSize := runtime.Stack(buf, true) + fmt.Printf("\nGoroutine stack trace (first 2048 bytes):\n") + if stackSize > 2048 { + fmt.Printf("%s...\n", buf[:2048]) + fmt.Printf("(truncated, total size: %d bytes)\n", stackSize) + } else { + fmt.Printf("%s\n", buf[:stackSize]) + } +} + +func printBasicInfo() { + fmt.Println("=== Performance Debug Info ===") + fmt.Printf("Go version: %s\n", runtime.Version()) + fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH) + fmt.Printf("CPUs: %d\n", runtime.NumCPU()) + fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0)) + fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine()) + + var m runtime.MemStats + runtime.ReadMemStats(&m) + fmt.Printf("Memory allocated: %d KB\n", m.Alloc/1024) + fmt.Printf("Total allocations: %d KB\n", m.TotalAlloc/1024) + fmt.Printf("GC runs: %d\n", m.NumGC) + + fmt.Println("\nUse --help to see available profiling options.") +} diff --git a/agent/cmd/server/cmd/root.go b/agent/cmd/server/cmd/root.go new file mode 100644 index 000000000..38adf9a79 --- /dev/null +++ b/agent/cmd/server/cmd/root.go @@ -0,0 +1,14 @@ +package cmd + +import ( + "github.com/1Panel-dev/1Panel/agent/server" + "github.com/spf13/cobra" +) + +var RootCmd = &cobra.Command{ + Use: "1panel-agent", + RunE: func(cmd *cobra.Command, args []string) error { + server.Start() + return nil + }, +} diff --git a/agent/cmd/server/main.go b/agent/cmd/server/main.go index 4a7364f7d..2aa7bcbb1 100644 --- a/agent/cmd/server/main.go +++ b/agent/cmd/server/main.go @@ -1,9 +1,9 @@ package main import ( - _ "net/http/pprof" - - "github.com/1Panel-dev/1Panel/agent/server" + "fmt" + "github.com/1Panel-dev/1Panel/agent/cmd/server/cmd" + "os" ) // @title 1Panel @@ -15,5 +15,8 @@ import ( // @host localhost // @BasePath /api/v2 func main() { - server.Start() + if err := cmd.RootCmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } } diff --git a/frontend/src/global/mimetype.ts b/frontend/src/global/mimetype.ts index c0a3007d7..5abedd220 100644 --- a/frontend/src/global/mimetype.ts +++ b/frontend/src/global/mimetype.ts @@ -224,7 +224,7 @@ export const DNSTypes = [ value: 'FreeMyIP', }, { - label: i18n.global.t('website.baiduCloud'), + label: i18n.global.t('ssl.baiduCloud'), value: 'BaiduCloud', }, {