@@ -18,6 +18,8 @@ import (
1818 "fmt"
1919 "log/slog"
2020 "os"
21+ "runtime/pprof"
22+ "strings"
2123
2224 "github.com/blinklabs-io/dingo/internal/config"
2325 "github.com/blinklabs-io/dingo/internal/version"
@@ -37,7 +39,9 @@ func slogPrintf(format string, v ...any) {
3739
3840var (
3941 globalFlags = struct {
40- debug bool
42+ cpuprofile string
43+ memprofile string
44+ debug bool
4145 }{}
4246 configFile string
4347)
@@ -72,6 +76,45 @@ func commonRun() *slog.Logger {
7276}
7377
7478func main () {
79+ // Parse profiling flags before cobra setup (handle both --flag=value and --flag value syntax)
80+ cpuprofile := ""
81+ memprofile := ""
82+ args := os .Args [1 :] // Skip program name
83+ for i := 0 ; i < len (args ); i ++ {
84+ arg := args [i ]
85+ switch {
86+ case strings .HasPrefix (arg , "--cpuprofile=" ):
87+ cpuprofile = strings .TrimPrefix (arg , "--cpuprofile=" )
88+ case arg == "--cpuprofile" && i + 1 < len (args ):
89+ cpuprofile = args [i + 1 ]
90+ i ++ // Skip next arg
91+ case strings .HasPrefix (arg , "--memprofile=" ):
92+ memprofile = strings .TrimPrefix (arg , "--memprofile=" )
93+ case arg == "--memprofile" && i + 1 < len (args ):
94+ memprofile = args [i + 1 ]
95+ i ++ // Skip next arg
96+ }
97+ }
98+
99+ // Initialize CPU profiling (starts immediately, stops on exit)
100+ if cpuprofile != "" {
101+ fmt .Fprintf (os .Stderr , "Starting CPU profiling to %s\n " , cpuprofile )
102+ f , err := os .Create (cpuprofile )
103+ if err != nil {
104+ fmt .Fprintf (os .Stderr , "could not create CPU profile: %v\n " , err )
105+ os .Exit (1 )
106+ }
107+ defer f .Close ()
108+ if err := pprof .StartCPUProfile (f ); err != nil {
109+ fmt .Fprintf (os .Stderr , "could not start CPU profile: %v\n " , err )
110+ os .Exit (1 )
111+ }
112+ defer func () {
113+ pprof .StopCPUProfile ()
114+ fmt .Fprintf (os .Stderr , "CPU profiling stopped\n " )
115+ }()
116+ }
117+
75118 rootCmd := & cobra.Command {
76119 Use : programName ,
77120 Run : func (cmd * cobra.Command , args []string ) {
@@ -89,6 +132,10 @@ func main() {
89132 BoolVarP (& globalFlags .debug , "debug" , "D" , false , "enable debug logging" )
90133 rootCmd .PersistentFlags ().
91134 StringVar (& configFile , "config" , "" , "path to config file" )
135+ rootCmd .PersistentFlags ().
136+ StringVar (& globalFlags .cpuprofile , "cpuprofile" , "" , "write cpu profile to file" )
137+ rootCmd .PersistentFlags ().
138+ StringVar (& globalFlags .memprofile , "memprofile" , "" , "write memory profile to file" )
92139
93140 rootCmd .PersistentPreRunE = func (cmd * cobra.Command , args []string ) error {
94141 cfg , err := config .LoadConfig (configFile )
@@ -109,4 +156,20 @@ func main() {
109156 // NOTE: we purposely don't display the error, since cobra will have already displayed it
110157 os .Exit (1 )
111158 }
159+
160+ // Finalize memory profiling (captures heap state at program end)
161+ if memprofile != "" {
162+ f , err := os .Create (memprofile )
163+ if err != nil {
164+ fmt .Fprintf (os .Stderr , "could not create memory profile: %v\n " , err )
165+ os .Exit (1 )
166+ }
167+ defer f .Close ()
168+ if err := pprof .WriteHeapProfile (f ); err != nil {
169+ fmt .Fprintf (os .Stderr , "could not write memory profile: %v\n " , err )
170+ f .Close () // Explicitly close before exit
171+ os .Exit (1 )
172+ }
173+ fmt .Fprintf (os .Stderr , "Memory profiling complete\n " )
174+ }
112175}
0 commit comments