Start begins CPU and/or memory profiling based on the provided file paths. Pass an empty string to skip the corresponding profile. The returned Stop function must be called to finalise the profiles.
(cpuProfile, memProfile string)
| 19 | // paths. Pass an empty string to skip the corresponding profile. |
| 20 | // The returned Stop function must be called to finalise the profiles. |
| 21 | func Start(cpuProfile, memProfile string) (Stop, error) { |
| 22 | var closers []func() error |
| 23 | |
| 24 | if cpuProfile != "" { |
| 25 | f, err := os.OpenFile(cpuProfile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600) |
| 26 | if err != nil { |
| 27 | return noop, fmt.Errorf("failed to create CPU profile: %w", err) |
| 28 | } |
| 29 | if err := pprof.StartCPUProfile(f); err != nil { |
| 30 | f.Close() |
| 31 | return noop, fmt.Errorf("failed to start CPU profile: %w", err) |
| 32 | } |
| 33 | closers = append(closers, func() error { |
| 34 | pprof.StopCPUProfile() |
| 35 | return f.Close() |
| 36 | }) |
| 37 | } |
| 38 | |
| 39 | if memProfile != "" { |
| 40 | closers = append(closers, func() error { |
| 41 | f, err := os.OpenFile(memProfile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600) |
| 42 | if err != nil { |
| 43 | return fmt.Errorf("failed to create memory profile: %w", err) |
| 44 | } |
| 45 | defer f.Close() |
| 46 | runtime.GC() |
| 47 | if err := pprof.WriteHeapProfile(f); err != nil { |
| 48 | return fmt.Errorf("failed to write memory profile: %w", err) |
| 49 | } |
| 50 | return nil |
| 51 | }) |
| 52 | } |
| 53 | |
| 54 | return func() error { |
| 55 | // Run in reverse order so CPU profile is stopped before mem profile is written. |
| 56 | var errs []error |
| 57 | for _, closeFn := range slices.Backward(closers) { |
| 58 | if err := closeFn(); err != nil { |
| 59 | errs = append(errs, err) |
| 60 | } |
| 61 | } |
| 62 | return errors.Join(errs...) |
| 63 | }, nil |
| 64 | } |
| 65 | |
| 66 | func noop() error { return nil } |