@@ -3,12 +3,6 @@ package tracker
33import (
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