package main import ( "flag" "fmt" "os" "os/signal" "runtime" "runtime/pprof" "strings" "syscall" "time" "github.com/freeflowuniverse/heroagent/pkg/system/stats" "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/process" ) // TestResult stores the results of a single test run type TestResult struct { StartTime time.Time EndTime time.Time SystemInfoTime time.Duration DiskStatsTime time.Duration ProcessTime time.Duration NetworkTime time.Duration HardwareTime time.Duration TotalTime time.Duration UserCPU float64 SystemCPU float64 TotalCPU float64 OverallCPU float64 MemoryUsageMB float32 NumGoroutines int } func main() { // Parse command line flags intervalPtr := flag.Int("interval", 5, "Interval between tests in seconds") sleepPtr := flag.Int("sleep", 0, "Sleep time between operations in milliseconds") cpuProfilePtr := flag.String("cpuprofile", "", "Write cpu profile to file") flag.Parse() // If CPU profiling is enabled, set it up if *cpuProfilePtr != "" { f, err := os.Create(*cpuProfilePtr) if err != nil { fmt.Printf("Error creating CPU profile: %v\n", err) os.Exit(1) } defer f.Close() if err := pprof.StartCPUProfile(f); err != nil { fmt.Printf("Error starting CPU profile: %v\n", err) os.Exit(1) } defer pprof.StopCPUProfile() } fmt.Println("StatsManager Performance Test") fmt.Println("============================") fmt.Printf("This test measures the performance of retrieving stats from Redis cache\n") fmt.Printf("It will run every %d seconds and print performance metrics\n", *intervalPtr) fmt.Printf("Sleep between operations: %d ms\n", *sleepPtr) fmt.Println("Press Ctrl+C to exit and view summary statistics") fmt.Println() // Create a new stats manager with Redis connection config := &stats.Config{ RedisAddr: "localhost:6379", RedisPassword: "", RedisDB: 0, Debug: false, QueueSize: 100, DefaultTimeout: 5 * time.Second, ExpirationTimes: map[string]time.Duration{ "system": 60 * time.Second, // System info expires after 60 seconds "disk": 300 * time.Second, // Disk info expires after 5 minutes "process": 60 * time.Second, // Process info expires after 1 minute "network": 30 * time.Second, // Network info expires after 30 seconds "hardware": 120 * time.Second, // Hardware stats expire after 2 minutes }, } manager, err := stats.NewStatsManager(config) if err != nil { fmt.Printf("Error creating stats manager: %v\n", err) os.Exit(1) } defer manager.Close() // Initialize the cache with initial values fmt.Println("Initializing cache with initial values...") _, _ = manager.GetSystemInfo() _, _ = manager.GetDiskStats() _, _ = manager.GetProcessStats(10) _ = manager.GetNetworkSpeedResult() _ = manager.GetHardwareStatsJSON() fmt.Println("Cache initialized. Starting performance test...") fmt.Println() // Set up signal handling for graceful shutdown sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // Create a ticker for running tests at the specified interval ticker := time.NewTicker(time.Duration(*intervalPtr) * time.Second) defer ticker.Stop() // Store the sleep duration between operations sleepDuration := time.Duration(*sleepPtr) * time.Millisecond // Get the current process for CPU and memory measurements currentProcess, err := process.NewProcess(int32(os.Getpid())) if err != nil { fmt.Printf("Error getting current process: %v\n", err) os.Exit(1) } // Store test results var results []TestResult // Print header fmt.Printf("%-20s %-20s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s\n", "Start Time", "End Time", "System(ms)", "Disk(ms)", "Process(ms)", "Network(ms)", "Hardware(ms)", "Total(ms)", "UserCPU(%)", "SysCPU(%)", "TotalCPU(%)", "Memory(MB)", "Goroutines") fmt.Println(strings.Repeat("-", 180)) // Run the test until interrupted for { select { case <-ticker.C: // Run a test and record the results result := runTest(manager, currentProcess, sleepDuration) results = append(results, result) // Print the result fmt.Printf("%-20s %-20s %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12d\n", result.StartTime.Format("15:04:05.000000"), result.EndTime.Format("15:04:05.000000"), float64(result.SystemInfoTime.Microseconds())/1000, float64(result.DiskStatsTime.Microseconds())/1000, float64(result.ProcessTime.Microseconds())/1000, float64(result.NetworkTime.Microseconds())/1000, float64(result.HardwareTime.Microseconds())/1000, float64(result.TotalTime.Microseconds())/1000, result.UserCPU, result.SystemCPU, result.TotalCPU, result.MemoryUsageMB, result.NumGoroutines) case <-sigChan: // Calculate and print summary statistics fmt.Println("\nTest Summary:") fmt.Println(strings.Repeat("-", 50)) var totalSystemTime, totalDiskTime, totalProcessTime, totalNetworkTime, totalHardwareTime, totalTime time.Duration var totalUserCPU, totalSystemCPU, totalCombinedCPU, totalOverallCPU float64 var totalMemory float32 for _, r := range results { totalSystemTime += r.SystemInfoTime totalDiskTime += r.DiskStatsTime totalProcessTime += r.ProcessTime totalNetworkTime += r.NetworkTime totalHardwareTime += r.HardwareTime totalTime += r.TotalTime totalUserCPU += r.UserCPU totalSystemCPU += r.SystemCPU totalCombinedCPU += r.TotalCPU totalOverallCPU += r.OverallCPU totalMemory += r.MemoryUsageMB } count := float64(len(results)) if count > 0 { fmt.Printf("Average System Info Time: %.2f ms\n", float64(totalSystemTime.Microseconds())/(count*1000)) fmt.Printf("Average Disk Stats Time: %.2f ms\n", float64(totalDiskTime.Microseconds())/(count*1000)) fmt.Printf("Average Process Time: %.2f ms\n", float64(totalProcessTime.Microseconds())/(count*1000)) fmt.Printf("Average Network Time: %.2f ms\n", float64(totalNetworkTime.Microseconds())/(count*1000)) fmt.Printf("Average Hardware Time: %.2f ms\n", float64(totalHardwareTime.Microseconds())/(count*1000)) fmt.Printf("Average Total Time: %.2f ms\n", float64(totalTime.Microseconds())/(count*1000)) fmt.Printf("Average User CPU: %.2f%%\n", totalUserCPU/count) fmt.Printf("Average System CPU: %.2f%%\n", totalSystemCPU/count) fmt.Printf("Average Process CPU: %.2f%%\n", totalCombinedCPU/count) fmt.Printf("Average Overall CPU: %.2f%%\n", totalOverallCPU/count) fmt.Printf("Average Memory Usage: %.2f MB\n", float64(totalMemory)/count) } fmt.Println("\nTest completed. Exiting...") return } } } // runTest runs a single test iteration and returns the results func runTest(manager *stats.StatsManager, proc *process.Process, sleepBetweenOps time.Duration) TestResult { // Get initial CPU times for the process initialTimes, _ := proc.Times() // Get initial overall CPU usage _, _ = cpu.Percent(0, false) // Discard initial reading, we'll only use the final reading result := TestResult{ StartTime: time.Now(), } // Measure total time totalStart := time.Now() // Measure system info time start := time.Now() _, _ = manager.GetSystemInfo() result.SystemInfoTime = time.Since(start) // Sleep between operations if configured if sleepBetweenOps > 0 { time.Sleep(sleepBetweenOps) } // Measure disk stats time start = time.Now() _, _ = manager.GetDiskStats() result.DiskStatsTime = time.Since(start) // Sleep between operations if configured if sleepBetweenOps > 0 { time.Sleep(sleepBetweenOps) } // Measure process stats time start = time.Now() _, _ = manager.GetProcessStats(10) result.ProcessTime = time.Since(start) // Sleep between operations if configured if sleepBetweenOps > 0 { time.Sleep(sleepBetweenOps) } // Measure network speed time start = time.Now() _ = manager.GetNetworkSpeedResult() result.NetworkTime = time.Since(start) // Sleep between operations if configured if sleepBetweenOps > 0 { time.Sleep(sleepBetweenOps) } // Measure hardware stats time start = time.Now() _ = manager.GetHardwareStatsJSON() result.HardwareTime = time.Since(start) // Record total time result.TotalTime = time.Since(totalStart) result.EndTime = time.Now() // Get final CPU times for the process finalTimes, _ := proc.Times() // Calculate CPU usage for this specific operation if initialTimes != nil && finalTimes != nil { result.UserCPU = (finalTimes.User - initialTimes.User) * 100 result.SystemCPU = (finalTimes.System - initialTimes.System) * 100 result.TotalCPU = result.UserCPU + result.SystemCPU } // Get overall CPU usage finalOverallCPU, _ := cpu.Percent(0, false) if len(finalOverallCPU) > 0 { result.OverallCPU = finalOverallCPU[0] } // Measure memory usage memInfo, _ := proc.MemoryInfo() if memInfo != nil { result.MemoryUsageMB = float32(memInfo.RSS) / (1024 * 1024) } // Record number of goroutines result.NumGoroutines = runtime.NumGoroutine() return result }