77using BenchmarkDotNet . Reports ;
88using BenchmarkDotNet . Running ;
99using BenchmarkDotNet . Validators ;
10+ using JetBrains . Annotations ;
1011using System ;
1112using System . Collections . Generic ;
13+ using System . ComponentModel ;
1214using System . Linq ;
15+ using System . Runtime . ExceptionServices ;
16+ using System . Threading ;
1317
1418namespace BenchmarkDotNet . Diagnosers
1519{
16- public class ExceptionDiagnoser : IDiagnoser
20+ public class ExceptionDiagnoser ( ExceptionDiagnoserConfig config ) : IInProcessDiagnoser
1721 {
18- public static readonly ExceptionDiagnoser Default = new ExceptionDiagnoser ( new ExceptionDiagnoserConfig ( displayExceptionsIfZeroValue : true ) ) ;
22+ public static readonly ExceptionDiagnoser Default = new ( new ExceptionDiagnoserConfig ( displayExceptionsIfZeroValue : true ) ) ;
1923
20- public ExceptionDiagnoser ( ExceptionDiagnoserConfig config ) => Config = config ;
24+ private readonly Dictionary < BenchmarkCase , long > results = [ ] ;
2125
22- public ExceptionDiagnoserConfig Config { get ; }
26+ public ExceptionDiagnoserConfig Config { get ; } = config ;
2327
24- public IEnumerable < string > Ids => new [ ] { nameof ( ExceptionDiagnoser ) } ;
28+ public IEnumerable < string > Ids => [ nameof ( ExceptionDiagnoser ) ] ;
2529
26- public IEnumerable < IExporter > Exporters => Array . Empty < IExporter > ( ) ;
30+ public IEnumerable < IExporter > Exporters => [ ] ;
2731
28- public IEnumerable < IAnalyser > Analysers => Array . Empty < IAnalyser > ( ) ;
32+ public IEnumerable < IAnalyser > Analysers => [ ] ;
2933
3034 public void DisplayResults ( ILogger logger ) { }
3135
32- public RunMode GetRunMode ( BenchmarkCase benchmarkCase ) => RunMode . NoOverhead ;
36+ public RunMode GetRunMode ( BenchmarkCase benchmarkCase ) => RunMode . ExtraIteration ;
3337
3438 public void Handle ( HostSignal signal , DiagnoserActionParameters parameters ) { }
3539
36- public IEnumerable < Metric > ProcessResults ( DiagnoserResults results )
40+ public IEnumerable < Metric > ProcessResults ( DiagnoserResults diagnoserResults )
3741 {
38- yield return new Metric ( new ExceptionsFrequencyMetricDescriptor ( Config ) , results . ExceptionFrequency ) ;
42+ if ( results . TryGetValue ( diagnoserResults . BenchmarkCase , out var exceptionsCount ) )
43+ {
44+ double totalOperations = diagnoserResults . Measurements . First ( m => m . IterationStage == IterationStage . Extra ) . Operations ;
45+ yield return new Metric ( new ExceptionsFrequencyMetricDescriptor ( Config ) , exceptionsCount / totalOperations ) ;
46+ }
3947 }
4048
41- public IEnumerable < ValidationError > Validate ( ValidationParameters validationParameters ) => Enumerable . Empty < ValidationError > ( ) ;
49+ public IEnumerable < ValidationError > Validate ( ValidationParameters validationParameters ) => [ ] ;
4250
43- internal class ExceptionsFrequencyMetricDescriptor : IMetricDescriptor
44- {
45- public ExceptionDiagnoserConfig Config { get ; }
46- public ExceptionsFrequencyMetricDescriptor ( ExceptionDiagnoserConfig config = null )
47- {
48- Config = config ;
49- }
51+ void IInProcessDiagnoser . DeserializeResults ( BenchmarkCase benchmarkCase , string serializedResults )
52+ => results . Add ( benchmarkCase , long . Parse ( serializedResults ) ) ;
5053
54+ InProcessDiagnoserHandlerData IInProcessDiagnoser . GetHandlerData ( BenchmarkCase benchmarkCase )
55+ => new ( typeof ( ExceptionDiagnoserInProcessHandler ) , null ) ;
56+
57+ internal class ExceptionsFrequencyMetricDescriptor ( ExceptionDiagnoserConfig config ) : IMetricDescriptor
58+ {
5159 public string Id => "ExceptionFrequency" ;
5260 public string DisplayName => Column . Exceptions ;
5361 public string Legend => "Exceptions thrown per single operation" ;
@@ -57,12 +65,37 @@ public ExceptionsFrequencyMetricDescriptor(ExceptionDiagnoserConfig config = nul
5765 public bool TheGreaterTheBetter => false ;
5866 public int PriorityInCategory => 0 ;
5967 public bool GetIsAvailable ( Metric metric )
68+ => config ? . DisplayExceptionsIfZeroValue == true || metric . Value > 0 ;
69+ }
70+ }
71+
72+ [ UsedImplicitly ]
73+ [ EditorBrowsable ( EditorBrowsableState . Never ) ]
74+ public sealed class ExceptionDiagnoserInProcessHandler : IInProcessDiagnoserHandler
75+ {
76+ private long exceptionsCount ;
77+
78+ void IInProcessDiagnoserHandler . Handle ( BenchmarkSignal signal , InProcessDiagnoserActionArgs args )
79+ {
80+ switch ( signal )
6081 {
61- if ( Config == null )
62- return metric . Value > 0 ;
63- else
64- return Config . DisplayExceptionsIfZeroValue || metric . Value > 0 ;
82+ case BenchmarkSignal . BeforeExtraIteration :
83+ AppDomain . CurrentDomain . FirstChanceException += OnFirstChanceException ;
84+ break ;
85+ case BenchmarkSignal . AfterExtraIteration :
86+ AppDomain . CurrentDomain . FirstChanceException -= OnFirstChanceException ;
87+ break ;
6588 }
6689 }
90+
91+ void IInProcessDiagnoserHandler . Initialize ( string ? serializedConfig ) { }
92+
93+ string IInProcessDiagnoserHandler . SerializeResults ( )
94+ => exceptionsCount . ToString ( ) ;
95+
96+ private void OnFirstChanceException ( object sender , FirstChanceExceptionEventArgs e )
97+ {
98+ Interlocked . Increment ( ref exceptionsCount ) ;
99+ }
67100 }
68101}
0 commit comments