44using BenchmarkDotNet . Engines ;
55using BenchmarkDotNet . Exporters ;
66using BenchmarkDotNet . Loggers ;
7+ using BenchmarkDotNet . Portability ;
78using BenchmarkDotNet . Reports ;
89using BenchmarkDotNet . Running ;
910using BenchmarkDotNet . Validators ;
11+ using JetBrains . Annotations ;
1012using System ;
1113using System . Collections . Generic ;
14+ using System . ComponentModel ;
1215using System . Linq ;
16+ using System . Runtime . CompilerServices ;
17+ using System . Runtime . ExceptionServices ;
18+ using System . Threading ;
1319
1420namespace BenchmarkDotNet . Diagnosers
1521{
16- public class ExceptionDiagnoser : IDiagnoser
22+ public class ExceptionDiagnoser ( ExceptionDiagnoserConfig config ) : IInProcessDiagnoser
1723 {
18- public static readonly ExceptionDiagnoser Default = new ExceptionDiagnoser ( new ExceptionDiagnoserConfig ( displayExceptionsIfZeroValue : true ) ) ;
24+ public static readonly ExceptionDiagnoser Default = new ( new ExceptionDiagnoserConfig ( displayExceptionsIfZeroValue : true ) ) ;
1925
20- public ExceptionDiagnoser ( ExceptionDiagnoserConfig config ) => Config = config ;
26+ private readonly Dictionary < BenchmarkCase , long > results = [ ] ;
2127
22- public ExceptionDiagnoserConfig Config { get ; }
28+ public ExceptionDiagnoserConfig Config { get ; } = config ;
2329
24- public IEnumerable < string > Ids => new [ ] { nameof ( ExceptionDiagnoser ) } ;
30+ public IEnumerable < string > Ids => [ nameof ( ExceptionDiagnoser ) ] ;
2531
26- public IEnumerable < IExporter > Exporters => Array . Empty < IExporter > ( ) ;
32+ public IEnumerable < IExporter > Exporters => [ ] ;
2733
28- public IEnumerable < IAnalyser > Analysers => Array . Empty < IAnalyser > ( ) ;
34+ public IEnumerable < IAnalyser > Analysers => [ ] ;
2935
3036 public void DisplayResults ( ILogger logger ) { }
3137
32- public RunMode GetRunMode ( BenchmarkCase benchmarkCase ) => RunMode . NoOverhead ;
38+ public RunMode GetRunMode ( BenchmarkCase benchmarkCase ) => RunMode . ExtraIteration ;
3339
40+ [ MethodImpl ( CodeGenHelper . AggressiveOptimizationOption ) ]
3441 public void Handle ( HostSignal signal , DiagnoserActionParameters parameters ) { }
3542
36- public IEnumerable < Metric > ProcessResults ( DiagnoserResults results )
43+ public IEnumerable < Metric > ProcessResults ( DiagnoserResults diagnoserResults )
3744 {
38- yield return new Metric ( new ExceptionsFrequencyMetricDescriptor ( Config ) , results . ExceptionFrequency ) ;
45+ if ( results . TryGetValue ( diagnoserResults . BenchmarkCase , out var exceptionsCount ) )
46+ {
47+ double totalOperations = diagnoserResults . Measurements . First ( m => m . IterationStage == IterationStage . Extra ) . Operations ;
48+ yield return new Metric ( new ExceptionsFrequencyMetricDescriptor ( Config ) , exceptionsCount / totalOperations ) ;
49+ }
3950 }
4051
41- public IEnumerable < ValidationError > Validate ( ValidationParameters validationParameters ) => Enumerable . Empty < ValidationError > ( ) ;
52+ public IEnumerable < ValidationError > Validate ( ValidationParameters validationParameters ) => [ ] ;
4253
43- internal class ExceptionsFrequencyMetricDescriptor : IMetricDescriptor
44- {
45- public ExceptionDiagnoserConfig Config { get ; }
46- public ExceptionsFrequencyMetricDescriptor ( ExceptionDiagnoserConfig config = null )
47- {
48- Config = config ;
49- }
54+ void IInProcessDiagnoser . DeserializeResults ( BenchmarkCase benchmarkCase , string serializedResults )
55+ => results . Add ( benchmarkCase , long . Parse ( serializedResults ) ) ;
5056
57+ InProcessDiagnoserHandlerData IInProcessDiagnoser . GetHandlerData ( BenchmarkCase benchmarkCase )
58+ => new ( typeof ( ExceptionDiagnoserInProcessHandler ) , null ) ;
59+
60+ internal class ExceptionsFrequencyMetricDescriptor ( ExceptionDiagnoserConfig config ) : IMetricDescriptor
61+ {
5162 public string Id => "ExceptionFrequency" ;
5263 public string DisplayName => Column . Exceptions ;
5364 public string Legend => "Exceptions thrown per single operation" ;
@@ -57,12 +68,39 @@ public ExceptionsFrequencyMetricDescriptor(ExceptionDiagnoserConfig config = nul
5768 public bool TheGreaterTheBetter => false ;
5869 public int PriorityInCategory => 0 ;
5970 public bool GetIsAvailable ( Metric metric )
71+ => config ? . DisplayExceptionsIfZeroValue == true || metric . Value > 0 ;
72+ }
73+ }
74+
75+ [ UsedImplicitly ]
76+ [ EditorBrowsable ( EditorBrowsableState . Never ) ]
77+ public sealed class ExceptionDiagnoserInProcessHandler : IInProcessDiagnoserHandler
78+ {
79+ private long exceptionsCount ;
80+
81+ [ MethodImpl ( CodeGenHelper . AggressiveOptimizationOption ) ]
82+ void IInProcessDiagnoserHandler . Handle ( BenchmarkSignal signal , InProcessDiagnoserActionArgs args )
83+ {
84+ switch ( signal )
6085 {
61- if ( Config == null )
62- return metric . Value > 0 ;
63- else
64- return Config . DisplayExceptionsIfZeroValue || metric . Value > 0 ;
86+ case BenchmarkSignal . BeforeExtraIteration :
87+ AppDomain . CurrentDomain . FirstChanceException += OnFirstChanceException ;
88+ break ;
89+ case BenchmarkSignal . AfterExtraIteration :
90+ AppDomain . CurrentDomain . FirstChanceException -= OnFirstChanceException ;
91+ break ;
6592 }
6693 }
94+
95+ void IInProcessDiagnoserHandler . Initialize ( string ? serializedConfig ) { }
96+
97+ string IInProcessDiagnoserHandler . SerializeResults ( )
98+ => exceptionsCount . ToString ( ) ;
99+
100+ [ MethodImpl ( CodeGenHelper . AggressiveOptimizationOption ) ]
101+ private void OnFirstChanceException ( object sender , FirstChanceExceptionEventArgs e )
102+ {
103+ Interlocked . Increment ( ref exceptionsCount ) ;
104+ }
67105 }
68106}
0 commit comments