提交 3de55924 编写于 作者: M Manish Vasani

Add a new command line compiler switch "/reportanalyzer" to report analyzer...

Add a new command line compiler switch "/reportanalyzer" to report analyzer execution times. Output is grouped by analyzer assemblies and is displayed in descending order of execution times.

NOTE: We do not display the total build time or the ratios between build time and analyzer execution time as the the actual wall clock time for analyzer execution is likely lesser due to multithreaded analyzer execution.
上级 00a59192
...@@ -4407,6 +4407,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ ...@@ -4407,6 +4407,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
diagnostics. diagnostics.
/errorlog:<file> Specify a file to log all compiler and analyzer /errorlog:<file> Specify a file to log all compiler and analyzer
diagnostics. diagnostics.
/reportanalyzer Report additional analyzer information, such as
execution time.
- LANGUAGE - - LANGUAGE -
/checked[+|-] Generate overflow checks /checked[+|-] Generate overflow checks
......
...@@ -103,6 +103,7 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas ...@@ -103,6 +103,7 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
List<string> features = new List<string>(); List<string> features = new List<string>();
string runtimeMetadataVersion = null; string runtimeMetadataVersion = null;
bool errorEndLocation = false; bool errorEndLocation = false;
bool reportAnalyzer = false;
CultureInfo preferredUILang = null; CultureInfo preferredUILang = null;
string touchedFilesPath = null; string touchedFilesPath = null;
var sqmSessionGuid = Guid.Empty; var sqmSessionGuid = Guid.Empty;
...@@ -892,6 +893,10 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas ...@@ -892,6 +893,10 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
errorEndLocation = true; errorEndLocation = true;
continue; continue;
case "reportanalyzer":
reportAnalyzer = true;
continue;
case "nostdlib": case "nostdlib":
case "nostdlib+": case "nostdlib+":
if (value != null) if (value != null)
...@@ -1111,7 +1116,8 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas ...@@ -1111,7 +1116,8 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
PrintFullPaths = printFullPaths, PrintFullPaths = printFullPaths,
ShouldIncludeErrorEndLocation = errorEndLocation, ShouldIncludeErrorEndLocation = errorEndLocation,
PreferredUILang = preferredUILang, PreferredUILang = preferredUILang,
SqmSessionGuid = sqmSessionGuid SqmSessionGuid = sqmSessionGuid,
ReportAnalyzer = reportAnalyzer
}; };
} }
......
...@@ -6563,6 +6563,32 @@ public void ErrorLineEnd() ...@@ -6563,6 +6563,32 @@ public void ErrorLineEnd()
Assert.Equal(stringStart, text.Substring(0, stringStart.Length)); Assert.Equal(stringStart, text.Substring(0, stringStart.Length));
} }
[Fact]
public void ReportAnalyzer()
{
var parsedArgs1 = DefaultParse(new[] { "a.cs", "/reportanalyzer" }, _baseDirectory);
Assert.True(parsedArgs1.ReportAnalyzer);
var parsedArgs2 = DefaultParse(new[] { "a.cs", "" }, _baseDirectory);
Assert.False(parsedArgs2.ReportAnalyzer);
}
[Fact]
public void ReportAnalyzerOutput()
{
var srcFile = Temp.CreateFile().WriteAllText(@"class C {}");
var srcDirectory = Path.GetDirectoryName(srcFile.Path);
var outWriter = new StringWriter(CultureInfo.InvariantCulture);
var csc = new MockCSharpCompiler(null, srcDirectory, new[] { "/reportanalyzer", "/t:library", "/a:" + Assembly.GetExecutingAssembly().Location, srcFile.Path });
var exitCode = csc.Run(outWriter);
Assert.Equal(0, exitCode);
var output = outWriter.ToString();
Assert.Contains(CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader, output, StringComparison.Ordinal);
Assert.Contains(new WarningDiagnosticAnalyzer().ToString(), output, StringComparison.Ordinal);
CleanupAllGeneratedFiles(srcFile.Path);
}
[Fact] [Fact]
public void ErrorPathsFromLineDirectives() public void ErrorPathsFromLineDirectives()
{ {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Collections;
using Roslyn.Utilities; using Roslyn.Utilities;
using System.Collections.Concurrent;
namespace Microsoft.CodeAnalysis.Diagnostics namespace Microsoft.CodeAnalysis.Diagnostics
{ {
...@@ -26,6 +27,7 @@ internal class AnalyzerExecutor ...@@ -26,6 +27,7 @@ internal class AnalyzerExecutor
private readonly AnalyzerManager _analyzerManager; private readonly AnalyzerManager _analyzerManager;
private readonly Func<DiagnosticAnalyzer, bool> _isCompilerAnalyzer; private readonly Func<DiagnosticAnalyzer, bool> _isCompilerAnalyzer;
private readonly ImmutableDictionary<DiagnosticAnalyzer, object> _singleThreadedAnalyzerToGateMap; private readonly ImmutableDictionary<DiagnosticAnalyzer, object> _singleThreadedAnalyzerToGateMap;
private readonly ConcurrentDictionary<DiagnosticAnalyzer, TimeSpan> _analyzerExecutionTimeMapOpt;
private readonly CancellationToken _cancellationToken; private readonly CancellationToken _cancellationToken;
...@@ -45,6 +47,7 @@ internal class AnalyzerExecutor ...@@ -45,6 +47,7 @@ internal class AnalyzerExecutor
/// <param name="singleThreadedAnalyzerToGateMap">Map from non-thread safe analyzers to unique gate objects. /// <param name="singleThreadedAnalyzerToGateMap">Map from non-thread safe analyzers to unique gate objects.
/// All analyzer callbacks for these analyzers will be guarded with a lock on the gate. /// All analyzer callbacks for these analyzers will be guarded with a lock on the gate.
/// </param> /// </param>
/// <param name="logExecutionTime">Flag indicating whether we need to log analyzer execution time.</param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
public static AnalyzerExecutor Create( public static AnalyzerExecutor Create(
Compilation compilation, Compilation compilation,
...@@ -54,9 +57,10 @@ internal class AnalyzerExecutor ...@@ -54,9 +57,10 @@ internal class AnalyzerExecutor
Func<DiagnosticAnalyzer, bool> isCompilerAnalyzer, Func<DiagnosticAnalyzer, bool> isCompilerAnalyzer,
AnalyzerManager analyzerManager, AnalyzerManager analyzerManager,
ImmutableDictionary<DiagnosticAnalyzer, object> singleThreadedAnalyzerToGateMap = default(ImmutableDictionary<DiagnosticAnalyzer, object>), ImmutableDictionary<DiagnosticAnalyzer, object> singleThreadedAnalyzerToGateMap = default(ImmutableDictionary<DiagnosticAnalyzer, object>),
bool logExecutionTime = false,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default(CancellationToken))
{ {
return new AnalyzerExecutor(compilation, analyzerOptions, addDiagnostic, onAnalyzerException, isCompilerAnalyzer, analyzerManager, singleThreadedAnalyzerToGateMap, cancellationToken); return new AnalyzerExecutor(compilation, analyzerOptions, addDiagnostic, onAnalyzerException, isCompilerAnalyzer, analyzerManager, singleThreadedAnalyzerToGateMap, logExecutionTime, cancellationToken);
} }
/// <summary> /// <summary>
...@@ -67,11 +71,13 @@ internal class AnalyzerExecutor ...@@ -67,11 +71,13 @@ internal class AnalyzerExecutor
/// Delegate can do custom tasks such as report the given analyzer exception diagnostic, report a non-fatal watson for the exception, etc. /// Delegate can do custom tasks such as report the given analyzer exception diagnostic, report a non-fatal watson for the exception, etc.
/// </param> /// </param>
/// <param name="analyzerManager">Analyzer manager to fetch supported diagnostics.</param> /// <param name="analyzerManager">Analyzer manager to fetch supported diagnostics.</param>
/// <param name="logExecutionTime">Flag indicating whether we need to log analyzer execution time.</param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
public static AnalyzerExecutor CreateForSupportedDiagnostics( public static AnalyzerExecutor CreateForSupportedDiagnostics(
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException,
AnalyzerManager analyzerManager, AnalyzerManager analyzerManager,
CancellationToken cancellationToken) bool logExecutionTime = false,
CancellationToken cancellationToken = default(CancellationToken))
{ {
return new AnalyzerExecutor( return new AnalyzerExecutor(
compilation: null, compilation: null,
...@@ -81,6 +87,7 @@ internal class AnalyzerExecutor ...@@ -81,6 +87,7 @@ internal class AnalyzerExecutor
singleThreadedAnalyzerToGateMap: ImmutableDictionary<DiagnosticAnalyzer, object>.Empty, singleThreadedAnalyzerToGateMap: ImmutableDictionary<DiagnosticAnalyzer, object>.Empty,
onAnalyzerException: onAnalyzerException, onAnalyzerException: onAnalyzerException,
analyzerManager: analyzerManager, analyzerManager: analyzerManager,
logExecutionTime: logExecutionTime,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
} }
...@@ -92,6 +99,7 @@ internal class AnalyzerExecutor ...@@ -92,6 +99,7 @@ internal class AnalyzerExecutor
Func<DiagnosticAnalyzer, bool> isCompilerAnalyzer, Func<DiagnosticAnalyzer, bool> isCompilerAnalyzer,
AnalyzerManager analyzerManager, AnalyzerManager analyzerManager,
ImmutableDictionary<DiagnosticAnalyzer, object> singleThreadedAnalyzerToGateMap, ImmutableDictionary<DiagnosticAnalyzer, object> singleThreadedAnalyzerToGateMap,
bool logExecutionTime,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
_compilation = compilation; _compilation = compilation;
...@@ -101,6 +109,7 @@ internal class AnalyzerExecutor ...@@ -101,6 +109,7 @@ internal class AnalyzerExecutor
_isCompilerAnalyzer = isCompilerAnalyzer; _isCompilerAnalyzer = isCompilerAnalyzer;
_analyzerManager = analyzerManager; _analyzerManager = analyzerManager;
_singleThreadedAnalyzerToGateMap = singleThreadedAnalyzerToGateMap ?? ImmutableDictionary<DiagnosticAnalyzer, object>.Empty; _singleThreadedAnalyzerToGateMap = singleThreadedAnalyzerToGateMap ?? ImmutableDictionary<DiagnosticAnalyzer, object>.Empty;
_analyzerExecutionTimeMapOpt = logExecutionTime ? new ConcurrentDictionary<DiagnosticAnalyzer, TimeSpan>() : null;
_cancellationToken = cancellationToken; _cancellationToken = cancellationToken;
} }
...@@ -108,6 +117,7 @@ internal class AnalyzerExecutor ...@@ -108,6 +117,7 @@ internal class AnalyzerExecutor
internal AnalyzerOptions AnalyzerOptions => _analyzerOptions; internal AnalyzerOptions AnalyzerOptions => _analyzerOptions;
internal CancellationToken CancellationToken => _cancellationToken; internal CancellationToken CancellationToken => _cancellationToken;
internal Action<Exception, DiagnosticAnalyzer, Diagnostic> OnAnalyzerException => _onAnalyzerException; internal Action<Exception, DiagnosticAnalyzer, Diagnostic> OnAnalyzerException => _onAnalyzerException;
internal ImmutableDictionary<DiagnosticAnalyzer, TimeSpan> AnalyzerExecutionTimes => _analyzerExecutionTimeMapOpt.ToImmutableDictionary();
/// <summary> /// <summary>
/// Executes the <see cref="DiagnosticAnalyzer.Initialize(AnalysisContext)"/> for the given analyzer. /// Executes the <see cref="DiagnosticAnalyzer.Initialize(AnalysisContext)"/> for the given analyzer.
...@@ -481,30 +491,39 @@ internal void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action analyz ...@@ -481,30 +491,39 @@ internal void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action analyz
{ {
lock(gate) lock(gate)
{ {
ExecuteAndCatchIfThrows(analyzer, analyze, _onAnalyzerException, _cancellationToken); ExecuteAndCatchIfThrows_NoLock(analyzer, analyze);
} }
} }
else else
{ {
ExecuteAndCatchIfThrows(analyzer, analyze, _onAnalyzerException, _cancellationToken); ExecuteAndCatchIfThrows_NoLock(analyzer, analyze);
} }
} }
private static void ExecuteAndCatchIfThrows( private void ExecuteAndCatchIfThrows_NoLock(DiagnosticAnalyzer analyzer, Action analyze)
DiagnosticAnalyzer analyzer,
Action analyze,
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException,
CancellationToken cancellationToken)
{ {
try try
{ {
Stopwatch timer = null;
if (_analyzerExecutionTimeMapOpt != null)
{
timer = Stopwatch.StartNew();
}
analyze(); analyze();
if (timer != null)
{
timer.Stop();
_analyzerExecutionTimeMapOpt.AddOrUpdate(analyzer, timer.Elapsed, (a, accumulatedTime) => accumulatedTime + timer.Elapsed);
}
} }
catch (Exception e) when (!IsCanceled(e, cancellationToken)) catch (Exception e) when (!IsCanceled(e, _cancellationToken))
{ {
// Diagnostic for analyzer exception. // Diagnostic for analyzer exception.
var diagnostic = GetAnalyzerExceptionDiagnostic(analyzer, e); var diagnostic = GetAnalyzerExceptionDiagnostic(analyzer, e);
onAnalyzerException(e, analyzer, diagnostic); _onAnalyzerException(e, analyzer, diagnostic);
} }
} }
......
...@@ -276,7 +276,7 @@ private static void TestDescriptorIsExceptionSafeCore(DiagnosticDescriptor descr ...@@ -276,7 +276,7 @@ private static void TestDescriptorIsExceptionSafeCore(DiagnosticDescriptor descr
var analyzer = new MyAnalyzer(descriptor); var analyzer = new MyAnalyzer(descriptor);
var exceptionDiagnostics = new List<Diagnostic>(); var exceptionDiagnostics = new List<Diagnostic>();
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = (ex, a, diag) => exceptionDiagnostics.Add(diag); Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = (ex, a, diag) => exceptionDiagnostics.Add(diag);
var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, AnalyzerManager.Instance, CancellationToken.None); var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, AnalyzerManager.Instance);
var descriptors = AnalyzerManager.Instance.GetSupportedDiagnosticDescriptors(analyzer, analyzerExecutor); var descriptors = AnalyzerManager.Instance.GetSupportedDiagnosticDescriptors(analyzer, analyzerExecutor);
Assert.Equal(1, descriptors.Length); Assert.Equal(1, descriptors.Length);
......
...@@ -585,8 +585,7 @@ protected internal virtual void AddResponseFileCommands(CommandLineBuilderExtens ...@@ -585,8 +585,7 @@ protected internal virtual void AddResponseFileCommands(CommandLineBuilderExtens
commandLine.AppendSwitchIfNotNull("/ruleset:", this.CodeAnalysisRuleSet); commandLine.AppendSwitchIfNotNull("/ruleset:", this.CodeAnalysisRuleSet);
commandLine.AppendSwitchIfNotNull("/errorlog:", this.ErrorLog); commandLine.AppendSwitchIfNotNull("/errorlog:", this.ErrorLog);
commandLine.AppendSwitchIfNotNull("/subsystemversion:", this.SubsystemVersion); commandLine.AppendSwitchIfNotNull("/subsystemversion:", this.SubsystemVersion);
// TODO: uncomment the below line once "/reportanalyzer" switch is added to compiler. commandLine.AppendWhenTrue("/reportanalyzer", this._store, "ReportAnalyzer");
//commandLine.AppendWhenTrue("/reportanalyzer", this._store, "ReportAnalyzer");
// If the strings "LogicalName" or "Access" ever change, make sure to search/replace everywhere in vsproject. // If the strings "LogicalName" or "Access" ever change, make sure to search/replace everywhere in vsproject.
commandLine.AppendSwitchIfNotNull("/resource:", this.Resources, new string[] { "LogicalName", "Access" }); commandLine.AppendSwitchIfNotNull("/resource:", this.Resources, new string[] { "LogicalName", "Access" });
commandLine.AppendSwitchIfNotNull("/target:", this.TargetType); commandLine.AppendSwitchIfNotNull("/target:", this.TargetType);
......
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // This code was generated by a tool.
// Runtime Version:4.0.30319.0 // Runtime Version:4.0.30319.42000
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
...@@ -70,6 +70,33 @@ internal class CodeAnalysisResources { ...@@ -70,6 +70,33 @@ internal class CodeAnalysisResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Time (s).
/// </summary>
internal static string AnalyzerExecutionTimeColumnHeader {
get {
return ResourceManager.GetString("AnalyzerExecutionTimeColumnHeader", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Analyzer.
/// </summary>
internal static string AnalyzerNameColumnHeader {
get {
return ResourceManager.GetString("AnalyzerNameColumnHeader", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Total analyzer execution time: {0} seconds..
/// </summary>
internal static string AnalyzerTotalExecutionTime {
get {
return ResourceManager.GetString("AnalyzerTotalExecutionTime", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Argument cannot be empty.. /// Looks up a localized string similar to Argument cannot be empty..
/// </summary> /// </summary>
...@@ -665,6 +692,15 @@ internal class CodeAnalysisResources { ...@@ -665,6 +692,15 @@ internal class CodeAnalysisResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to NOTE: Elapsed time may be less than analyzer execution time because analyzers can run concurrently..
/// </summary>
internal static string MultithreadedAnalyzerExecutionNote {
get {
return ResourceManager.GetString("MultithreadedAnalyzerExecutionNote", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Name cannot be empty.. /// Looks up a localized string similar to Name cannot be empty..
/// </summary> /// </summary>
......
...@@ -457,4 +457,16 @@ ...@@ -457,4 +457,16 @@
<data name="MissingKeepAlive" xml:space="preserve"> <data name="MissingKeepAlive" xml:space="preserve">
<value>Missing argument for '/keepalive' option.</value> <value>Missing argument for '/keepalive' option.</value>
</data> </data>
<data name="AnalyzerTotalExecutionTime" xml:space="preserve">
<value>Total analyzer execution time: {0} seconds.</value>
</data>
<data name="MultithreadedAnalyzerExecutionNote" xml:space="preserve">
<value>NOTE: Elapsed time may be less than analyzer execution time because analyzers can run concurrently.</value>
</data>
<data name="AnalyzerExecutionTimeColumnHeader" xml:space="preserve">
<value>Time (s)</value>
</data>
<data name="AnalyzerNameColumnHeader" xml:space="preserve">
<value>Analyzer</value>
</data>
</root> </root>
\ No newline at end of file
...@@ -115,6 +115,11 @@ public abstract class CommandLineArguments ...@@ -115,6 +115,11 @@ public abstract class CommandLineArguments
/// </summary> /// </summary>
public ImmutableArray<CommandLineSourceFile> AdditionalFiles { get; internal set; } public ImmutableArray<CommandLineSourceFile> AdditionalFiles { get; internal set; }
/// <value>
/// Report additional information related to analyzers, such as analyzer execution time.
/// </value>
public bool ReportAnalyzer { get; internal set; }
/// <summary> /// <summary>
/// If true, prepend the command line header logo during /// If true, prepend the command line header logo during
/// <see cref="CommonCompiler.Run"/>. /// <see cref="CommonCompiler.Run"/>.
......
...@@ -355,6 +355,7 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat ...@@ -355,6 +355,7 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat
CancellationTokenSource analyzerCts = null; CancellationTokenSource analyzerCts = null;
AnalyzerManager analyzerManager = null; AnalyzerManager analyzerManager = null;
AnalyzerDriver analyzerDriver = null;
try try
{ {
Func<ImmutableArray<Diagnostic>> getAnalyzerDiagnostics = null; Func<ImmutableArray<Diagnostic>> getAnalyzerDiagnostics = null;
...@@ -365,7 +366,7 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat ...@@ -365,7 +366,7 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat
var analyzerExceptionDiagnostics = new ConcurrentSet<Diagnostic>(); var analyzerExceptionDiagnostics = new ConcurrentSet<Diagnostic>();
Action<Diagnostic> addExceptionDiagnostic = diagnostic => analyzerExceptionDiagnostics.Add(diagnostic); Action<Diagnostic> addExceptionDiagnostic = diagnostic => analyzerExceptionDiagnostics.Add(diagnostic);
var analyzerOptions = new AnalyzerOptions(ImmutableArray<AdditionalText>.CastUp(additionalTextFiles)); var analyzerOptions = new AnalyzerOptions(ImmutableArray<AdditionalText>.CastUp(additionalTextFiles));
var analyzerDriver = AnalyzerDriver.Create(compilation, analyzers, analyzerOptions, analyzerManager, addExceptionDiagnostic, out compilation, analyzerCts.Token); analyzerDriver = AnalyzerDriver.Create(compilation, analyzers, analyzerOptions, analyzerManager, addExceptionDiagnostic, Arguments.ReportAnalyzer, out compilation, analyzerCts.Token);
getAnalyzerDiagnostics = () => getAnalyzerDiagnostics = () =>
{ {
...@@ -525,9 +526,17 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat ...@@ -525,9 +526,17 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat
{ {
analyzerCts.Cancel(); analyzerCts.Cancel();
if (analyzerManager != null)
{
// Clear cached analyzer descriptors and unregister exception handlers hooked up to the LocalizableString fields of the associated descriptors. // Clear cached analyzer descriptors and unregister exception handlers hooked up to the LocalizableString fields of the associated descriptors.
analyzerManager.ClearAnalyzerState(analyzers); analyzerManager.ClearAnalyzerState(analyzers);
} }
if (Arguments.ReportAnalyzer && analyzerDriver != null && compilation != null)
{
ReportAnalyzerExecutionTime(consoleOutput, analyzerDriver, Culture, compilation.Options.ConcurrentBuild);
}
}
} }
return Succeeded; return Succeeded;
...@@ -545,6 +554,71 @@ private ImmutableArray<AdditionalTextFile> ResolveAdditionalFilesFromArguments(L ...@@ -545,6 +554,71 @@ private ImmutableArray<AdditionalTextFile> ResolveAdditionalFilesFromArguments(L
return builder.ToImmutableArray(); return builder.ToImmutableArray();
} }
private static void ReportAnalyzerExecutionTime(TextWriter consoleOutput, AnalyzerDriver analyzerDriver, CultureInfo culture, bool isConcurrentBuild)
{
Debug.Assert(analyzerDriver.AnalyzerExecutionTimes != null);
if (analyzerDriver.AnalyzerExecutionTimes.IsEmpty)
{
return;
}
var totalAnalyzerExecutionTime = analyzerDriver.AnalyzerExecutionTimes.Sum(kvp => kvp.Value.TotalSeconds);
Func<double, string> getFormattedTime = d => d.ToString("##0.000", culture);
consoleOutput.WriteLine();
consoleOutput.WriteLine(string.Format(CodeAnalysisResources.AnalyzerTotalExecutionTime, getFormattedTime(totalAnalyzerExecutionTime)));
if (isConcurrentBuild)
{
consoleOutput.WriteLine(CodeAnalysisResources.MultithreadedAnalyzerExecutionNote);
}
var analyzersByAssembly = analyzerDriver.AnalyzerExecutionTimes
.GroupBy(kvp => kvp.Key.GetType().GetTypeInfo().Assembly)
.OrderByDescending(kvp => kvp.Sum(entry => entry.Value.Ticks));
consoleOutput.WriteLine();
getFormattedTime = d => d < 0.001 ?
string.Format(culture, "{0,8:<0.000}", 0.001) :
string.Format(culture, "{0,8:##0.000}", d);
Func<int, string> getFormattedPercentage = i => string.Format("{0,5}", i < 1 ? "<1" : i.ToString());
Func<string, string> getFormattedAnalyzerName = s => " " + s;
// Table header
var analyzerTimeColumn = string.Format("{0,8}", CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader);
var analyzerPercentageColumn = string.Format("{0,5}", "%");
var analyzerNameColumn = getFormattedAnalyzerName(CodeAnalysisResources.AnalyzerNameColumnHeader);
consoleOutput.WriteLine(analyzerTimeColumn + analyzerPercentageColumn + analyzerNameColumn);
// Table rows grouped by assembly.
foreach (var analyzerGroup in analyzersByAssembly)
{
var executionTime = analyzerGroup.Sum(kvp => kvp.Value.TotalSeconds);
var percentage = (int)(executionTime * 100 / totalAnalyzerExecutionTime);
analyzerTimeColumn = getFormattedTime(executionTime);
analyzerPercentageColumn = getFormattedPercentage(percentage);
analyzerNameColumn = getFormattedAnalyzerName(analyzerGroup.Key.FullName);
consoleOutput.WriteLine(analyzerTimeColumn + analyzerPercentageColumn + analyzerNameColumn);
// Rows for each diagnostic analyzer in the assembly.
foreach (var kvp in analyzerGroup.OrderByDescending(kvp => kvp.Value))
{
executionTime = kvp.Value.TotalSeconds;
percentage = (int)(executionTime * 100 / totalAnalyzerExecutionTime);
analyzerTimeColumn = getFormattedTime(executionTime);
analyzerPercentageColumn = getFormattedPercentage(percentage);
analyzerNameColumn = getFormattedAnalyzerName(" " + kvp.Key.ToString());
consoleOutput.WriteLine(analyzerTimeColumn + analyzerPercentageColumn + analyzerNameColumn);
}
consoleOutput.WriteLine();
}
}
private void GenerateSqmData(CompilationOptions compilationOptions, ImmutableArray<Diagnostic> diagnostics) private void GenerateSqmData(CompilationOptions compilationOptions, ImmutableArray<Diagnostic> diagnostics)
{ {
// Generate SQM data file for Compilers // Generate SQM data file for Compilers
......
...@@ -66,7 +66,7 @@ internal abstract class AnalyzerDriver : IDisposable ...@@ -66,7 +66,7 @@ internal abstract class AnalyzerDriver : IDisposable
/// Finally, it initializes and starts the <see cref="_primaryTask"/> for the driver. /// Finally, it initializes and starts the <see cref="_primaryTask"/> for the driver.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// NOTE: This method must only be invoked from <see cref="AnalyzerDriver.Create(Compilation, ImmutableArray{DiagnosticAnalyzer}, AnalyzerOptions, AnalyzerManager, Action{Diagnostic}, out Compilation, CancellationToken)"/>. /// NOTE: This method must only be invoked from <see cref="AnalyzerDriver.Create(Compilation, ImmutableArray{DiagnosticAnalyzer}, AnalyzerOptions, AnalyzerManager, Action{Diagnostic}, Boolean, out Compilation, CancellationToken)"/>.
/// </remarks> /// </remarks>
private void Initialize(Compilation comp, AnalyzerExecutor analyzerExecutor, CancellationToken cancellationToken) private void Initialize(Compilation comp, AnalyzerExecutor analyzerExecutor, CancellationToken cancellationToken)
{ {
...@@ -144,6 +144,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken) ...@@ -144,6 +144,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken)
/// <param name="options">Options that are passed to analyzers.</param> /// <param name="options">Options that are passed to analyzers.</param>
/// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param> /// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param>
/// <param name="addExceptionDiagnostic">Delegate to add diagnostics generated for exceptions from third party analyzers.</param> /// <param name="addExceptionDiagnostic">Delegate to add diagnostics generated for exceptions from third party analyzers.</param>
/// <param name="reportAnalyzer">Report additional information related to analyzers, such as analyzer execution time.</param>
/// <param name="newCompilation">The new compilation with the analyzer driver attached.</param> /// <param name="newCompilation">The new compilation with the analyzer driver attached.</param>
/// <param name="cancellationToken">A cancellation token that can be used to abort analysis.</param> /// <param name="cancellationToken">A cancellation token that can be used to abort analysis.</param>
/// <returns>A newly created analyzer driver</returns> /// <returns>A newly created analyzer driver</returns>
...@@ -157,6 +158,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken) ...@@ -157,6 +158,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken)
AnalyzerOptions options, AnalyzerOptions options,
AnalyzerManager analyzerManager, AnalyzerManager analyzerManager,
Action<Diagnostic> addExceptionDiagnostic, Action<Diagnostic> addExceptionDiagnostic,
bool reportAnalyzer,
out Compilation newCompilation, out Compilation newCompilation,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
...@@ -178,7 +180,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken) ...@@ -178,7 +180,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken)
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException =
(ex, analyzer, diagnostic) => addExceptionDiagnostic?.Invoke(diagnostic); (ex, analyzer, diagnostic) => addExceptionDiagnostic?.Invoke(diagnostic);
return Create(compilation, analyzers, options, analyzerManager, onAnalyzerException, out newCompilation, cancellationToken: cancellationToken); return Create(compilation, analyzers, options, analyzerManager, onAnalyzerException, reportAnalyzer, out newCompilation, cancellationToken: cancellationToken);
} }
// internal for testing purposes // internal for testing purposes
...@@ -188,6 +190,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken) ...@@ -188,6 +190,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken)
AnalyzerOptions options, AnalyzerOptions options,
AnalyzerManager analyzerManager, AnalyzerManager analyzerManager,
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException,
bool reportAnalyzer,
out Compilation newCompilation, out Compilation newCompilation,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
...@@ -214,7 +217,14 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken) ...@@ -214,7 +217,14 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken)
// Assume all analyzers are non-thread safe. // Assume all analyzers are non-thread safe.
var singleThreadedAnalyzerToGateMap = ImmutableDictionary.CreateRange(analyzers.Select(a => KeyValuePair.Create(a, new object()))); var singleThreadedAnalyzerToGateMap = ImmutableDictionary.CreateRange(analyzers.Select(a => KeyValuePair.Create(a, new object())));
var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, newOnAnalyzerException, IsCompilerAnalyzer, analyzerManager, singleThreadedAnalyzerToGateMap, cancellationToken); if (reportAnalyzer)
{
// If we are reporting detailed analyzer performance numbers, then do a dummy invocation of Compilation.GetTypeByMetadataName API upfront.
// This API seems to cause a severe hit for the first analyzer invoking it and hence introduces lot of noise in the computed analyzer execution times.
var unused = newCompilation.GetTypeByMetadataName("System.Object");
}
var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, newOnAnalyzerException, IsCompilerAnalyzer, analyzerManager, singleThreadedAnalyzerToGateMap, reportAnalyzer, cancellationToken);
analyzerDriver.Initialize(newCompilation, analyzerExecutor, cancellationToken); analyzerDriver.Initialize(newCompilation, analyzerExecutor, cancellationToken);
...@@ -274,6 +284,8 @@ public async Task<ImmutableArray<Diagnostic>> GetDiagnosticsAsync() ...@@ -274,6 +284,8 @@ public async Task<ImmutableArray<Diagnostic>> GetDiagnosticsAsync()
/// </summary> /// </summary>
public Task WhenCompletedTask => _primaryTask; public Task WhenCompletedTask => _primaryTask;
internal ImmutableDictionary<DiagnosticAnalyzer, TimeSpan> AnalyzerExecutionTimes => analyzerExecutor.AnalyzerExecutionTimes;
private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>> MakeSymbolActionsByKind() private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>> MakeSymbolActionsByKind()
{ {
var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>>(); var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>>();
......
...@@ -32,7 +32,7 @@ public CompilationWithAnalyzers(Compilation compilation, ImmutableArray<Diagnost ...@@ -32,7 +32,7 @@ public CompilationWithAnalyzers(Compilation compilation, ImmutableArray<Diagnost
{ {
_cancellationToken = cancellationToken; _cancellationToken = cancellationToken;
_exceptionDiagnostics = new ConcurrentSet<Diagnostic>(); _exceptionDiagnostics = new ConcurrentSet<Diagnostic>();
_driver = AnalyzerDriver.Create(compilation, analyzers, options, AnalyzerManager.Instance, AddExceptionDiagnostic, out _compilation, _cancellationToken); _driver = AnalyzerDriver.Create(compilation, analyzers, options, AnalyzerManager.Instance, AddExceptionDiagnostic, false, out _compilation, _cancellationToken);
} }
private void AddExceptionDiagnostic(Diagnostic diagnostic) private void AddExceptionDiagnostic(Diagnostic diagnostic)
...@@ -120,7 +120,7 @@ public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, C ...@@ -120,7 +120,7 @@ public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, C
Action<Exception, DiagnosticAnalyzer, Diagnostic> voidHandler = (ex, a, diag) => { }; Action<Exception, DiagnosticAnalyzer, Diagnostic> voidHandler = (ex, a, diag) => { };
onAnalyzerException = onAnalyzerException ?? voidHandler; onAnalyzerException = onAnalyzerException ?? voidHandler;
var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, AnalyzerManager.Instance, CancellationToken.None); var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, AnalyzerManager.Instance);
return AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, options, AnalyzerManager.Instance, analyzerExecutor); return AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, options, AnalyzerManager.Instance, analyzerExecutor);
} }
......
...@@ -2203,6 +2203,7 @@ Microsoft.CodeAnalysis.CommandLineArguments.PdbPath.get -> string ...@@ -2203,6 +2203,7 @@ Microsoft.CodeAnalysis.CommandLineArguments.PdbPath.get -> string
Microsoft.CodeAnalysis.CommandLineArguments.PreferredUILang.get -> System.Globalization.CultureInfo Microsoft.CodeAnalysis.CommandLineArguments.PreferredUILang.get -> System.Globalization.CultureInfo
Microsoft.CodeAnalysis.CommandLineArguments.PrintFullPaths.get -> bool Microsoft.CodeAnalysis.CommandLineArguments.PrintFullPaths.get -> bool
Microsoft.CodeAnalysis.CommandLineArguments.ReferencePaths.get -> System.Collections.Immutable.ImmutableArray<string> Microsoft.CodeAnalysis.CommandLineArguments.ReferencePaths.get -> System.Collections.Immutable.ImmutableArray<string>
Microsoft.CodeAnalysis.CommandLineArguments.ReportAnalyzer.get -> bool
Microsoft.CodeAnalysis.CommandLineArguments.ResolveAnalyzerReferences(Microsoft.CodeAnalysis.IAnalyzerAssemblyLoader analyzerLoader) -> System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.Diagnostics.AnalyzerReference> Microsoft.CodeAnalysis.CommandLineArguments.ResolveAnalyzerReferences(Microsoft.CodeAnalysis.IAnalyzerAssemblyLoader analyzerLoader) -> System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.Diagnostics.AnalyzerReference>
Microsoft.CodeAnalysis.CommandLineArguments.ResolveMetadataReferences(Microsoft.CodeAnalysis.MetadataReferenceResolver metadataResolver) -> System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.MetadataReference> Microsoft.CodeAnalysis.CommandLineArguments.ResolveMetadataReferences(Microsoft.CodeAnalysis.MetadataReferenceResolver metadataResolver) -> System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.MetadataReference>
Microsoft.CodeAnalysis.CommandLineArguments.ScriptArguments.get -> System.Collections.Immutable.ImmutableArray<string> Microsoft.CodeAnalysis.CommandLineArguments.ScriptArguments.get -> System.Collections.Immutable.ImmutableArray<string>
......
...@@ -2032,5 +2032,195 @@ public void OnlyStartsOneServer() ...@@ -2032,5 +2032,195 @@ public void OnlyStartsOneServer()
result = ProcessLauncher.Run(_csharpCompilerClientExecutable, ""); result = ProcessLauncher.Run(_csharpCompilerClientExecutable, "");
Assert.Equal(1, GetProcessesByFullPath(_compilerServerExecutable).Count); Assert.Equal(1, GetProcessesByFullPath(_compilerServerExecutable).Count);
} }
// A dictionary with name and contents of all the files we want to create for the ReportAnalyzerMSBuild test.
private Dictionary<string, string> ReportAnalyzerMsBuildFiles => new Dictionary<string, string> {
{ "HelloSolution.sln",
@"
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""HelloLib"", ""HelloLib.csproj"", ""{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}""
EndProject
Project(""{F184B08F-C81C-45F6-A57F-5ABD9991F28F}"") = ""VBLib"", ""VBLib.vbproj"", ""{F21C894B-28E5-4212-8AF7-C8E0E5455737}""
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Debug|x86.ActiveCfg = Debug|Any CPU
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Release|Any CPU.Build.0 = Release|Any CPU
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}.Release|x86.ActiveCfg = Release|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Debug|x86.ActiveCfg = Debug|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Release|Any CPU.Build.0 = Release|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F21C894B-28E5-4212-8AF7-C8E0E5455737}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
"},
{ "HelloLib.csproj",
@"<?xml version=""1.0"" encoding=""utf-8""?>
<Project ToolsVersion=""4.0"" DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
<UsingTask TaskName=""Microsoft.CodeAnalysis.BuildTasks.Csc"" AssemblyFile=""" + _buildTaskDll + @""" />
<PropertyGroup>
<Configuration Condition="" '$(Configuration)' == '' "">Debug</Configuration>
<Platform Condition="" '$(Platform)' == '' "">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{C1170A4A-80CF-4B4F-AA58-2FAEA9158D31}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>HelloLib</RootNamespace>
<AssemblyName>HelloLib</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition="" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ReportAnalyzer>True</ReportAnalyzer>
</PropertyGroup>
<ItemGroup>
<Reference Include=""System"" />
<Reference Include=""System.Core"" />
<Reference Include=""System.Xml.Linq"" />
<Reference Include=""System.Xml"" />
</ItemGroup>
<ItemGroup>
<Compile Include=""HelloLib.cs"" />
</ItemGroup>
<ItemGroup>
<Folder Include=""Properties\"" />
</ItemGroup>
<Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />
<Import Project=""$(MyMSBuildToolsPath)\Microsoft.CSharp.Core.targets"" />
</Project>"},
{ "HelloLib.cs",
@"public class $P {}"},
{ "VBLib.vbproj",
@"<?xml version=""1.0"" encoding=""utf-8""?>
<Project ToolsVersion=""4.0"" DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
<UsingTask TaskName=""Microsoft.CodeAnalysis.BuildTasks.Vbc"" AssemblyFile=""" + _buildTaskDll + @""" />
<PropertyGroup>
<Configuration Condition="" '$(Configuration)' == '' "">Debug</Configuration>
<Platform Condition="" '$(Platform)' == '' "">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>
</SchemaVersion>
<ProjectGuid>{F21C894B-28E5-4212-8AF7-C8E0E5455737}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>VBLib</RootNamespace>
<AssemblyName>VBLib</AssemblyName>
<FileAlignment>512</FileAlignment>
<MyType>Windows</MyType>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition="" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>VBLib.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "">
<DebugType>pdbonly</DebugType>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DocumentationFile>VBLib.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup>
<OptionExplicit>On</OptionExplicit>
</PropertyGroup>
<PropertyGroup>
<OptionCompare>Binary</OptionCompare>
</PropertyGroup>
<PropertyGroup>
<OptionStrict>Off</OptionStrict>
</PropertyGroup>
<PropertyGroup>
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<PropertyGroup>
<ReportAnalyzer>True</ReportAnalyzer>
</PropertyGroup>
<ItemGroup>
<Reference Include=""System"" />
</ItemGroup>
<ItemGroup>
<Import Include=""Microsoft.VisualBasic"" />
<Import Include=""System"" />
<Import Include=""System.Collections.Generic"" />
</ItemGroup>
<ItemGroup>
<Compile Include=""VBLib.vb"" />
</ItemGroup>
<Import Project=""$(MSBuildToolsPath)\Microsoft.VisualBasic.targets"" />
<Import Project=""$(MyMSBuildToolsPath)\Microsoft.VisualBasic.Core.targets"" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
-->
</Project>"},
{ "VBLib.vb",
@"
Public Class $P
End Class
"}
};
[Fact]
public void ReportAnalyzerMSBuild()
{
string arguments = string.Format(@"/m /nr:false /t:Rebuild /p:UseRoslyn=1 HelloSolution.sln");
var result = RunCommandLineCompiler(MSBuildExecutable, arguments, _tempDirectory, ReportAnalyzerMsBuildFiles,
new Dictionary<string, string>
{ { "MyMSBuildToolsPath", Path.GetDirectoryName(typeof(CompilerServerUnitTests).Assembly.Location) } });
Assert.True(result.ExitCode != 0);
Assert.Contains("/reportanalyzer", result.Output);
}
} }
} }
...@@ -145,6 +145,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ...@@ -145,6 +145,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim sqmsessionguid As Guid = Nothing Dim sqmsessionguid As Guid = Nothing
Dim touchedFilesPath As String = Nothing Dim touchedFilesPath As String = Nothing
Dim features = New List(Of String)() Dim features = New List(Of String)()
Dim reportAnalyzer As Boolean = False
' Process ruleset files first so that diagnostic severity settings specified on the command line via ' Process ruleset files first so that diagnostic severity settings specified on the command line via
' /nowarn and /warnaserror can override diagnostic severity settings specified in the ruleset file. ' /nowarn and /warnaserror can override diagnostic severity settings specified in the ruleset file.
...@@ -937,6 +938,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ...@@ -937,6 +938,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
UnimplementedSwitch(diagnostics, name) UnimplementedSwitch(diagnostics, name)
Continue For Continue For
Case "reportanalyzer"
reportAnalyzer = True
Continue For
Case "nostdlib" Case "nostdlib"
If value IsNot Nothing Then If value IsNot Nothing Then
Exit Select Exit Select
...@@ -1213,7 +1218,8 @@ lVbRuntimePlus: ...@@ -1213,7 +1218,8 @@ lVbRuntimePlus:
.EmitPdb = emitPdb, .EmitPdb = emitPdb,
.DefaultCoreLibraryReference = defaultCoreLibraryReference, .DefaultCoreLibraryReference = defaultCoreLibraryReference,
.PreferredUILang = preferredUILang, .PreferredUILang = preferredUILang,
.SqmSessionGuid = sqmsessionguid .SqmSessionGuid = sqmsessionguid,
.ReportAnalyzer = reportAnalyzer
} }
End Function End Function
......
'------------------------------------------------------------------------------ '------------------------------------------------------------------------------
' <auto-generated> ' <auto-generated>
' This code was generated by a tool. ' This code was generated by a tool.
' Runtime Version:4.0.30319.0 ' Runtime Version:4.0.30319.42000
' '
' Changes to this file may cause incorrect behavior and will be lost if ' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated. ' the code is regenerated.
......
...@@ -5062,7 +5062,8 @@ ...@@ -5062,7 +5062,8 @@
diagnostics. diagnostics.
/errorlog:&lt;file&gt; Specify a file to log all compiler and analyzer /errorlog:&lt;file&gt; Specify a file to log all compiler and analyzer
diagnostics. diagnostics.
/reportanalyzer Report additional analyzer information, such as
execution time.
- LANGUAGE - - LANGUAGE -
/define:&lt;symbol_list&gt; Declare global conditional compilation /define:&lt;symbol_list&gt; Declare global conditional compilation
symbol(s). symbol_list:name=value,... symbol(s). symbol_list:name=value,...
......
...@@ -7110,6 +7110,31 @@ out ...@@ -7110,6 +7110,31 @@ out
Assert.Equal(expected:=ReportDiagnostic.Suppress, actual:=arguments.CompilationOptions.SpecificDiagnosticOptions("Test001")) Assert.Equal(expected:=ReportDiagnostic.Suppress, actual:=arguments.CompilationOptions.SpecificDiagnosticOptions("Test001"))
End Sub End Sub
<Fact>
Public Sub ReportAnalyzer()
Dim args1 = DefaultParse({"/reportanalyzer", "a.vb"}, _baseDirectory)
Assert.True(args1.ReportAnalyzer)
Dim args2 = DefaultParse({"", "a.vb"}, _baseDirectory)
Assert.False(args2.ReportAnalyzer)
End Sub
<Fact>
Public Sub ReportAnalyzerOutput()
Dim source As String = Temp.CreateFile().WriteAllText(<text>
Class C
End Class
</text>.Value).Path
Dim vbc = New MockVisualBasicCompiler(Nothing, _baseDirectory, {"/reportanalyzer", "/t:library", "/a:" + Assembly.GetExecutingAssembly().Location, source})
Dim outWriter = New StringWriter()
Dim exitCode = vbc.Run(outWriter, Nothing)
Assert.Equal(0, exitCode)
Dim output = outWriter.ToString()
Assert.Contains(New WarningDiagnosticAnalyzer().ToString(), output, StringComparison.Ordinal)
Assert.Contains(CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader, output, StringComparison.Ordinal)
CleanupAllGeneratedFiles(source)
End Sub
End Class End Class
<DiagnosticAnalyzer(LanguageNames.VisualBasic)> <DiagnosticAnalyzer(LanguageNames.VisualBasic)>
......
...@@ -73,7 +73,7 @@ private static string GetAssemblyQualifiedNameWithoutVersion(Type type) ...@@ -73,7 +73,7 @@ private static string GetAssemblyQualifiedNameWithoutVersion(Type type)
Action<Exception, DiagnosticAnalyzer, Diagnostic> defaultOnAnalyzerException = (ex, a, diagnostic) => Action<Exception, DiagnosticAnalyzer, Diagnostic> defaultOnAnalyzerException = (ex, a, diagnostic) =>
OnAnalyzerException_NoTelemetryLogging(ex, a, diagnostic, hostDiagnosticUpdateSource); OnAnalyzerException_NoTelemetryLogging(ex, a, diagnostic, hostDiagnosticUpdateSource);
return AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException ?? defaultOnAnalyzerException, AnalyzerManager.Instance, cancellationToken); return AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException ?? defaultOnAnalyzerException, AnalyzerManager.Instance, cancellationToken: cancellationToken);
} }
internal static void OnAnalyzerException_NoTelemetryLogging( internal static void OnAnalyzerException_NoTelemetryLogging(
......
...@@ -167,7 +167,7 @@ public static TCompilation VerifyDiagnostics<TCompilation>(this TCompilation c, ...@@ -167,7 +167,7 @@ public static TCompilation VerifyDiagnostics<TCompilation>(this TCompilation c,
} }
Compilation newCompilation; Compilation newCompilation;
var driver = AnalyzerDriver.Create(c, analyzersArray, options, AnalyzerManager.Instance, onAnalyzerException, out newCompilation, CancellationToken.None); var driver = AnalyzerDriver.Create(c, analyzersArray, options, AnalyzerManager.Instance, onAnalyzerException, false, out newCompilation, CancellationToken.None);
var discarded = newCompilation.GetDiagnostics(); var discarded = newCompilation.GetDiagnostics();
diagnostics = driver.GetDiagnosticsAsync().Result.AddRange(exceptionDiagnostics); diagnostics = driver.GetDiagnosticsAsync().Result.AddRange(exceptionDiagnostics);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册