Skip to content

Commit b75135e

Browse files
Merge pull request #88 from AlexsanderHamir/refactors
closes #81
2 parents ab1041f + dd2b4f2 commit b75135e

File tree

2 files changed

+235
-241
lines changed

2 files changed

+235
-241
lines changed

engine/tracker/api.go

Lines changed: 0 additions & 241 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@ package tracker
33
import (
44
"fmt"
55
"log/slog"
6-
"strings"
7-
8-
"math"
9-
10-
"github.com/AlexsanderHamir/prof/internal"
11-
"github.com/AlexsanderHamir/prof/parser"
126
)
137

148
// trackAutoSelections holds all the user selections for tracking
@@ -51,7 +45,6 @@ func RunTrackAuto(selections *Selections) error {
5145

5246
report.ChooseOutputFormat(selections.OutputFormat)
5347

54-
// Apply CI/CD filtering and thresholds
5548
if err = applyCIConfiguration(report, selections); err != nil {
5649
return err
5750
}
@@ -85,237 +78,3 @@ func RunTrackManual(selections *Selections) error {
8578

8679
return nil
8780
}
88-
89-
// applyCIConfiguration applies CI/CD configuration to the performance report
90-
func applyCIConfiguration(report *ProfileChangeReport, selections *Selections) error {
91-
// Load CI/CD configuration
92-
cfg, err := internal.LoadFromFile(internal.ConfigFilename)
93-
if err != nil {
94-
slog.Info("No CI/CD configuration found, using command-line settings only")
95-
// Fall back to command-line threshold logic
96-
return applyCommandLineThresholds(report, selections)
97-
}
98-
99-
// Apply CI/CD filtering
100-
report.ApplyCIConfiguration(cfg.CIConfig, selections.BenchmarkName)
101-
102-
// Check if CLI flags were provided for regression checking
103-
cliFlagsProvided := selections.UseThreshold || selections.RegressionThreshold > 0.0
104-
105-
if cliFlagsProvided {
106-
// User provided CLI flags, use them (with CI/CD config as fallback)
107-
return applyCommandLineThresholds(report, selections)
108-
}
109-
110-
// No CLI flags provided, use CI/CD config only
111-
slog.Info("No CLI regression flags provided, using CI/CD configuration settings")
112-
return applyCICDThresholdsOnly(report, selections, cfg.CIConfig)
113-
}
114-
115-
// applyCommandLineThresholds applies the legacy command-line threshold logic
116-
func applyCommandLineThresholds(report *ProfileChangeReport, selections *Selections) error {
117-
if selections.UseThreshold && selections.RegressionThreshold > 0.0 {
118-
worst := report.WorstRegression()
119-
if worst != nil && worst.FlatChangePercent >= selections.RegressionThreshold {
120-
return fmt.Errorf("performance regression %.2f%% in %s exceeds threshold %.2f%%", worst.FlatChangePercent, worst.FunctionName, selections.RegressionThreshold)
121-
}
122-
}
123-
return nil
124-
}
125-
126-
// applyCICDThresholdsOnly applies CI/CD specific threshold logic only, without CLI flags
127-
func applyCICDThresholdsOnly(report *ProfileChangeReport, selections *Selections, cicdConfig *internal.CIConfig) error {
128-
benchmarkName := selections.BenchmarkName
129-
if benchmarkName == "" {
130-
benchmarkName = "unknown"
131-
}
132-
133-
// Get effective regression threshold
134-
effectiveThreshold := getEffectiveRegressionThreshold(cicdConfig, benchmarkName, 0.0) // Use 0.0 for no CLI threshold
135-
136-
// Get minimum change threshold
137-
minChangeThreshold := getMinChangeThreshold(cicdConfig, benchmarkName)
138-
139-
// Check if we should fail on improvements
140-
failOnImprovement := shouldFailOnImprovement(cicdConfig, benchmarkName)
141-
142-
// Apply thresholds
143-
if effectiveThreshold > 0.0 {
144-
worst := report.WorstRegression()
145-
if worst != nil && worst.FlatChangePercent >= effectiveThreshold {
146-
// Check if function should be ignored by CI/CD config
147-
if !shouldIgnoreFunction(cicdConfig, worst.FunctionName, benchmarkName) {
148-
return fmt.Errorf("performance regression %.2f%% in %s exceeds CI/CD threshold %.2f%%",
149-
worst.FlatChangePercent, worst.FunctionName, effectiveThreshold)
150-
}
151-
}
152-
}
153-
154-
// Check for improvements if configured to fail on them
155-
if failOnImprovement {
156-
best := report.BestImprovement()
157-
if best != nil && math.Abs(best.FlatChangePercent) >= minChangeThreshold {
158-
if !shouldIgnoreFunction(cicdConfig, best.FunctionName, benchmarkName) {
159-
return fmt.Errorf("unexpected performance improvement %.2f%% in %s (configured to fail on improvements)",
160-
math.Abs(best.FlatChangePercent), best.FunctionName)
161-
}
162-
}
163-
}
164-
165-
// Check minimum change threshold
166-
if minChangeThreshold > 0.0 {
167-
worst := report.WorstRegression()
168-
if worst != nil && worst.FlatChangePercent < minChangeThreshold {
169-
slog.Info("Performance regression below minimum threshold, not failing CI/CD",
170-
"function", worst.FunctionName,
171-
"change", worst.FlatChangePercent,
172-
"threshold", minChangeThreshold)
173-
}
174-
}
175-
176-
return nil
177-
}
178-
179-
// Helper functions for CI/CD configuration
180-
func getEffectiveRegressionThreshold(cicdConfig *internal.CIConfig, benchmarkName string, commandLineThreshold float64) float64 {
181-
if cicdConfig == nil {
182-
return commandLineThreshold
183-
}
184-
185-
// Check benchmark-specific config first
186-
if benchmarkConfig, exists := cicdConfig.Benchmarks[benchmarkName]; exists && benchmarkConfig.MaxRegressionThreshold > 0 {
187-
if commandLineThreshold > 0 {
188-
if commandLineThreshold < benchmarkConfig.MaxRegressionThreshold {
189-
return commandLineThreshold
190-
}
191-
return benchmarkConfig.MaxRegressionThreshold
192-
}
193-
return benchmarkConfig.MaxRegressionThreshold
194-
}
195-
196-
// Fall back to global config
197-
if cicdConfig.Global != nil && cicdConfig.Global.MaxRegressionThreshold > 0 {
198-
if commandLineThreshold > 0 {
199-
if commandLineThreshold < cicdConfig.Global.MaxRegressionThreshold {
200-
return commandLineThreshold
201-
}
202-
return cicdConfig.Global.MaxRegressionThreshold
203-
}
204-
return cicdConfig.Global.MaxRegressionThreshold
205-
}
206-
207-
return commandLineThreshold
208-
}
209-
210-
func getMinChangeThreshold(cicdConfig *internal.CIConfig, benchmarkName string) float64 {
211-
if cicdConfig == nil {
212-
return 0.0
213-
}
214-
215-
// Check benchmark-specific config first
216-
if benchmarkConfig, exists := cicdConfig.Benchmarks[benchmarkName]; exists && benchmarkConfig.MinChangeThreshold > 0 {
217-
return benchmarkConfig.MinChangeThreshold
218-
}
219-
220-
// Fall back to global config
221-
if cicdConfig.Global != nil && cicdConfig.Global.MinChangeThreshold > 0 {
222-
return cicdConfig.Global.MinChangeThreshold
223-
}
224-
225-
return 0.0
226-
}
227-
228-
func shouldFailOnImprovement(cicdConfig *internal.CIConfig, benchmarkName string) bool {
229-
if cicdConfig == nil {
230-
return false
231-
}
232-
233-
// Check benchmark-specific config first
234-
if benchmarkConfig, exists := cicdConfig.Benchmarks[benchmarkName]; exists {
235-
return benchmarkConfig.FailOnImprovement
236-
}
237-
238-
// Fall back to global config
239-
if cicdConfig.Global != nil {
240-
return cicdConfig.Global.FailOnImprovement
241-
}
242-
243-
return false
244-
}
245-
246-
func shouldIgnoreFunction(cicdConfig *internal.CIConfig, functionName string, benchmarkName string) bool {
247-
if cicdConfig == nil {
248-
return false
249-
}
250-
251-
// Check benchmark-specific config first
252-
if benchmarkConfig, exists := cicdConfig.Benchmarks[benchmarkName]; exists {
253-
if shouldIgnoreFunctionByConfig(&benchmarkConfig, functionName) {
254-
return true
255-
}
256-
}
257-
258-
// Fall back to global config
259-
if cicdConfig.Global != nil {
260-
return shouldIgnoreFunctionByConfig(cicdConfig.Global, functionName)
261-
}
262-
263-
return false
264-
}
265-
266-
func shouldIgnoreFunctionByConfig(config *internal.CITrackingConfig, functionName string) bool {
267-
if config == nil {
268-
return false
269-
}
270-
271-
// Check exact function name matches
272-
for _, ignoredFunc := range config.IgnoreFunctions {
273-
if functionName == ignoredFunc {
274-
return true
275-
}
276-
}
277-
278-
// Check prefix matches
279-
for _, ignoredPrefix := range config.IgnorePrefixes {
280-
if strings.HasPrefix(functionName, ignoredPrefix) {
281-
return true
282-
}
283-
}
284-
285-
return false
286-
}
287-
288-
// CheckPerformanceDifferences creates the profile report by comparing data from prof's auto run.
289-
func CheckPerformanceDifferences(selections *Selections) (*ProfileChangeReport, error) {
290-
binFilePathBaseLine, binFilePathCurrent := chooseFileLocations(selections)
291-
292-
lineObjsBaseline, err := parser.TurnLinesIntoObjectsV2(binFilePathBaseLine)
293-
if err != nil {
294-
return nil, fmt.Errorf("couldn't get objs for path: %s, error: %w", binFilePathBaseLine, err)
295-
}
296-
297-
lineObjsCurrent, err := parser.TurnLinesIntoObjectsV2(binFilePathCurrent)
298-
if err != nil {
299-
return nil, fmt.Errorf("couldn't get objs for path: %s, error: %w", binFilePathCurrent, err)
300-
}
301-
302-
matchingMap := createMapFromLineObjects(lineObjsBaseline)
303-
304-
pgp := &ProfileChangeReport{}
305-
for _, currentObj := range lineObjsCurrent {
306-
baseLineObj, matchNotFound := matchingMap[currentObj.FnName]
307-
if !matchNotFound {
308-
continue
309-
}
310-
311-
var changeResult *FunctionChangeResult
312-
changeResult, err = detectChangeBetweenTwoObjects(baseLineObj, currentObj)
313-
if err != nil {
314-
return nil, fmt.Errorf("detectChangeBetweenTwoObjects failed: %w", err)
315-
}
316-
317-
pgp.FunctionChanges = append(pgp.FunctionChanges, changeResult)
318-
}
319-
320-
return pgp, nil
321-
}

0 commit comments

Comments
 (0)