Spaces:
Build error
Build error
| package profiler | |
| import ( | |
| "context" | |
| "fmt" | |
| "os" | |
| "runtime" | |
| "strconv" | |
| "time" | |
| "log/slog" | |
| "math" | |
| ) | |
| // Profile represents performance metrics for an operation | |
| type Profile struct { | |
| Operation string `json:"operation"` | |
| Duration time.Duration `json:"duration_ns"` | |
| MemoryBefore uint64 `json:"memory_before_bytes"` | |
| MemoryAfter uint64 `json:"memory_after_bytes"` | |
| MemoryDelta int64 `json:"memory_delta_bytes"` | |
| LinesCount int `json:"lines_count,omitempty"` | |
| BytesCount int64 `json:"bytes_count,omitempty"` | |
| Timestamp time.Time `json:"timestamp"` | |
| } | |
| // String returns a human-readable representation of the profile | |
| func (p *Profile) String() string { | |
| return fmt.Sprintf("[%s] %s: duration=%v, memory_delta=%+dB, lines=%d, bytes=%d", | |
| p.Timestamp.Format("15:04:05.000"), | |
| p.Operation, | |
| p.Duration, | |
| p.MemoryDelta, | |
| p.LinesCount, | |
| p.BytesCount, | |
| ) | |
| } | |
| func safeMemoryDelta(after, before uint64) int64 { | |
| if after > math.MaxInt64 || before > math.MaxInt64 { | |
| if after >= before { | |
| diff := after - before | |
| if diff > math.MaxInt64 { | |
| return math.MaxInt64 | |
| } | |
| return int64(diff) | |
| } | |
| diff := before - after | |
| if diff > math.MaxInt64 { | |
| return -math.MaxInt64 | |
| } | |
| return -int64(diff) | |
| } | |
| return int64(after) - int64(before) | |
| } | |
| // Profiler provides minimal performance profiling capabilities | |
| type Profiler struct { | |
| logger *slog.Logger | |
| enabled bool | |
| } | |
| // New creates a new Profiler instance | |
| func New(logger *slog.Logger, enabled bool) *Profiler { | |
| return &Profiler{ | |
| logger: logger, | |
| enabled: enabled, | |
| } | |
| } | |
| // ProfileFunc profiles a function execution | |
| func (p *Profiler) ProfileFunc(ctx context.Context, operation string, fn func() error) (*Profile, error) { | |
| if !p.enabled { | |
| return nil, fn() | |
| } | |
| profile := &Profile{ | |
| Operation: operation, | |
| Timestamp: time.Now(), | |
| } | |
| var memBefore runtime.MemStats | |
| runtime.ReadMemStats(&memBefore) | |
| profile.MemoryBefore = memBefore.Alloc | |
| start := time.Now() | |
| err := fn() | |
| profile.Duration = time.Since(start) | |
| var memAfter runtime.MemStats | |
| runtime.ReadMemStats(&memAfter) | |
| profile.MemoryAfter = memAfter.Alloc | |
| profile.MemoryDelta = safeMemoryDelta(memAfter.Alloc, memBefore.Alloc) | |
| if p.logger != nil { | |
| p.logger.InfoContext(ctx, "Performance profile", "profile", profile.String()) | |
| } | |
| return profile, err | |
| } | |
| // ProfileFuncWithMetrics profiles a function execution and captures additional metrics | |
| func (p *Profiler) ProfileFuncWithMetrics(ctx context.Context, operation string, fn func() (int, int64, error)) (*Profile, error) { | |
| if !p.enabled { | |
| _, _, err := fn() | |
| return nil, err | |
| } | |
| profile := &Profile{ | |
| Operation: operation, | |
| Timestamp: time.Now(), | |
| } | |
| var memBefore runtime.MemStats | |
| runtime.ReadMemStats(&memBefore) | |
| profile.MemoryBefore = memBefore.Alloc | |
| start := time.Now() | |
| lines, bytes, err := fn() | |
| profile.Duration = time.Since(start) | |
| profile.LinesCount = lines | |
| profile.BytesCount = bytes | |
| var memAfter runtime.MemStats | |
| runtime.ReadMemStats(&memAfter) | |
| profile.MemoryAfter = memAfter.Alloc | |
| profile.MemoryDelta = safeMemoryDelta(memAfter.Alloc, memBefore.Alloc) | |
| if p.logger != nil { | |
| p.logger.InfoContext(ctx, "Performance profile", "profile", profile.String()) | |
| } | |
| return profile, err | |
| } | |
| // Start begins timing an operation and returns a function to complete the profiling | |
| func (p *Profiler) Start(ctx context.Context, operation string) func(lines int, bytes int64) *Profile { | |
| if !p.enabled { | |
| return func(int, int64) *Profile { return nil } | |
| } | |
| profile := &Profile{ | |
| Operation: operation, | |
| Timestamp: time.Now(), | |
| } | |
| var memBefore runtime.MemStats | |
| runtime.ReadMemStats(&memBefore) | |
| profile.MemoryBefore = memBefore.Alloc | |
| start := time.Now() | |
| return func(lines int, bytes int64) *Profile { | |
| profile.Duration = time.Since(start) | |
| profile.LinesCount = lines | |
| profile.BytesCount = bytes | |
| var memAfter runtime.MemStats | |
| runtime.ReadMemStats(&memAfter) | |
| profile.MemoryAfter = memAfter.Alloc | |
| profile.MemoryDelta = safeMemoryDelta(memAfter.Alloc, memBefore.Alloc) | |
| if p.logger != nil { | |
| p.logger.InfoContext(ctx, "Performance profile", "profile", profile.String()) | |
| } | |
| return profile | |
| } | |
| } | |
| var globalProfiler *Profiler | |
| // IsProfilingEnabled checks if profiling is enabled via environment variables | |
| func IsProfilingEnabled() bool { | |
| if enabled, err := strconv.ParseBool(os.Getenv("GITHUB_MCP_PROFILING_ENABLED")); err == nil { | |
| return enabled | |
| } | |
| return false | |
| } | |
| // Init initializes the global profiler | |
| func Init(logger *slog.Logger, enabled bool) { | |
| globalProfiler = New(logger, enabled) | |
| } | |
| // InitFromEnv initializes the global profiler using environment variables | |
| func InitFromEnv(logger *slog.Logger) { | |
| globalProfiler = New(logger, IsProfilingEnabled()) | |
| } | |
| // ProfileFunc profiles a function using the global profiler | |
| func ProfileFunc(ctx context.Context, operation string, fn func() error) (*Profile, error) { | |
| if globalProfiler == nil { | |
| return nil, fn() | |
| } | |
| return globalProfiler.ProfileFunc(ctx, operation, fn) | |
| } | |
| // ProfileFuncWithMetrics profiles a function with metrics using the global profiler | |
| func ProfileFuncWithMetrics(ctx context.Context, operation string, fn func() (int, int64, error)) (*Profile, error) { | |
| if globalProfiler == nil { | |
| _, _, err := fn() | |
| return nil, err | |
| } | |
| return globalProfiler.ProfileFuncWithMetrics(ctx, operation, fn) | |
| } | |
| // Start begins timing using the global profiler | |
| func Start(ctx context.Context, operation string) func(int, int64) *Profile { | |
| if globalProfiler == nil { | |
| return func(int, int64) *Profile { return nil } | |
| } | |
| return globalProfiler.Start(ctx, operation) | |
| } | |