Skip to content

Commit 828e1f8

Browse files
committed
Share core analysis logic between standalone and normal analysis
1 parent 15e26e1 commit 828e1f8

File tree

2 files changed

+149
-120
lines changed

2 files changed

+149
-120
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs

Lines changed: 148 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public static ExitCode Run(string[] args)
8787
var pathTransformer = new PathTransformer(canonicalPathCache);
8888

8989
using var analyser = new NonStandaloneAnalyser(new LogProgressMonitor(logger), logger, options.AssemblySensitiveTrap, pathTransformer);
90-
using var references = new BlockingCollection<MetadataReference>();
90+
9191
try
9292
{
9393
var compilerVersion = new CompilerVersion(options);
@@ -120,80 +120,7 @@ public static ExitCode Run(string[] args)
120120
return ExitCode.Ok;
121121
}
122122

123-
var referenceTasks = ResolveReferences(compilerArguments, analyser, canonicalPathCache, references);
124-
125-
var syntaxTrees = new List<SyntaxTree>();
126-
var syntaxTreeTasks = ReadSyntaxTrees(
127-
compilerArguments.SourceFiles.
128-
Select(src => canonicalPathCache.GetCanonicalPath(src.Path)),
129-
analyser,
130-
compilerArguments.ParseOptions,
131-
compilerArguments.Encoding,
132-
syntaxTrees);
133-
134-
var sw1 = new Stopwatch();
135-
sw1.Start();
136-
137-
Parallel.Invoke(
138-
new ParallelOptions { MaxDegreeOfParallelism = options.Threads },
139-
referenceTasks.Interleave(syntaxTreeTasks).ToArray());
140-
141-
if (syntaxTrees.Count == 0)
142-
{
143-
logger.Log(Severity.Error, " No source files");
144-
++analyser.CompilationErrors;
145-
return ExitCode.Failed;
146-
}
147-
148-
// csc.exe (CSharpCompiler.cs) also provides CompilationOptions
149-
// .WithMetadataReferenceResolver(),
150-
// .WithXmlReferenceResolver() and
151-
// .WithSourceReferenceResolver().
152-
// These would be needed if we hadn't explicitly provided the source/references
153-
// already.
154-
var compilation = CSharpCompilation.Create(
155-
compilerArguments.CompilationName,
156-
syntaxTrees,
157-
references,
158-
compilerArguments.CompilationOptions.
159-
WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default).
160-
WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths))
161-
);
162-
163-
analyser.EndInitialize(compilerArguments, options, compilation);
164-
analyser.AnalyseCompilation();
165-
analyser.AnalyseReferences();
166-
167-
foreach (var tree in compilation.SyntaxTrees)
168-
{
169-
analyser.AnalyseTree(tree);
170-
}
171-
172-
var currentProcess = Process.GetCurrentProcess();
173-
var cpuTime1 = currentProcess.TotalProcessorTime;
174-
var userTime1 = currentProcess.UserProcessorTime;
175-
sw1.Stop();
176-
logger.Log(Severity.Info, " Models constructed in {0}", sw1.Elapsed);
177-
178-
var sw2 = new Stopwatch();
179-
sw2.Start();
180-
analyser.PerformExtraction(options.Threads);
181-
sw2.Stop();
182-
var cpuTime2 = currentProcess.TotalProcessorTime;
183-
var userTime2 = currentProcess.UserProcessorTime;
184-
185-
var performance = new Entities.PerformanceMetrics()
186-
{
187-
Frontend = new Entities.Timings() { Elapsed = sw1.Elapsed, Cpu = cpuTime1, User = userTime1 },
188-
Extractor = new Entities.Timings() { Elapsed = sw2.Elapsed, Cpu = cpuTime2 - cpuTime1, User = userTime2 - userTime1 },
189-
Total = new Entities.Timings() { Elapsed = stopwatch.Elapsed, Cpu = cpuTime2, User = userTime2 },
190-
PeakWorkingSet = currentProcess.PeakWorkingSet64
191-
};
192-
193-
analyser.LogPerformance(performance);
194-
logger.Log(Severity.Info, " Extraction took {0}", sw2.Elapsed);
195-
196-
return analyser.TotalErrors == 0 ? ExitCode.Ok : ExitCode.Errors;
123+
return AnalyseNonStandalone(analyser, compilerArguments, options, canonicalPathCache, stopwatch);
197124
}
198125
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
199126
{
@@ -321,73 +248,175 @@ public static void ExtractStandalone(
321248
ILogger logger,
322249
CommonOptions options)
323250
{
251+
var stopwatch = new Stopwatch();
252+
stopwatch.Start();
253+
324254
var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
325255
var pathTransformer = new PathTransformer(canonicalPathCache);
326256

327257
using var analyser = new StandaloneAnalyser(pm, logger, false, pathTransformer);
328-
using var references = new BlockingCollection<MetadataReference>();
329258
try
330259
{
331-
var referenceTasks = referencePaths.Select<string, Action>(path => () =>
332-
{
333-
var reference = MetadataReference.CreateFromFile(path);
334-
references.Add(reference);
335-
});
260+
AnalyseStandalone(analyser, sources, referencePaths, options, pm, stopwatch);
261+
}
262+
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
263+
{
264+
analyser.Logger.Log(Severity.Error, " Unhandled exception: {0}", ex);
265+
}
266+
}
336267

337-
var syntaxTrees = new List<SyntaxTree>();
338-
var syntaxTreeTasks = ReadSyntaxTrees(sources, analyser, null, null, syntaxTrees);
268+
private static ExitCode Analyse(Stopwatch stopwatch, Analyser analyser, CommonOptions options,
269+
Func<BlockingCollection<MetadataReference>, IEnumerable<Action>> getResolvedReferenceTasks,
270+
Func<Analyser, List<SyntaxTree>, IEnumerable<Action>> getSyntaxTreeTasks,
271+
Func<IEnumerable<SyntaxTree>, IEnumerable<MetadataReference>, CSharpCompilation> getCompilation,
272+
Action<CSharpCompilation, CommonOptions> initializeAnalyser,
273+
Action analyseCompilation,
274+
Action<Entities.PerformanceMetrics> logPerformance,
275+
Action postProcess)
276+
{
277+
using var references = new BlockingCollection<MetadataReference>();
278+
var referenceTasks = getResolvedReferenceTasks(references);
339279

340-
var sw = new Stopwatch();
341-
sw.Start();
280+
var syntaxTrees = new List<SyntaxTree>();
281+
var syntaxTreeTasks = getSyntaxTreeTasks(analyser, syntaxTrees);
342282

343-
Parallel.Invoke(
344-
new ParallelOptions { MaxDegreeOfParallelism = options.Threads },
345-
referenceTasks.Interleave(syntaxTreeTasks).ToArray());
283+
var sw = new Stopwatch();
284+
sw.Start();
346285

347-
if (syntaxTrees.Count == 0)
286+
Parallel.Invoke(
287+
new ParallelOptions { MaxDegreeOfParallelism = options.Threads },
288+
referenceTasks.Interleave(syntaxTreeTasks).ToArray());
289+
290+
if (syntaxTrees.Count == 0)
291+
{
292+
analyser.Logger.Log(Severity.Error, " No source files");
293+
++analyser.CompilationErrors;
294+
if (analyser is NonStandaloneAnalyser)
348295
{
349-
analyser.Logger.Log(Severity.Error, " No source files");
350-
++analyser.CompilationErrors;
296+
return ExitCode.Failed;
351297
}
298+
}
352299

353-
var compilation = CSharpCompilation.Create(
354-
"csharp.dll",
355-
syntaxTrees,
356-
references
357-
);
300+
var compilation = getCompilation(syntaxTrees, references);
358301

359-
analyser.InitializeStandalone(compilation, options);
360-
analyser.AnalyseReferences();
302+
initializeAnalyser(compilation, options);
303+
analyseCompilation();
304+
analyser.AnalyseReferences();
361305

362-
foreach (var tree in compilation.SyntaxTrees)
363-
{
364-
analyser.AnalyseTree(tree);
365-
}
306+
foreach (var tree in compilation.SyntaxTrees)
307+
{
308+
analyser.AnalyseTree(tree);
309+
}
366310

367-
sw.Stop();
368-
analyser.Logger.Log(Severity.Info, " Models constructed in {0}", sw.Elapsed);
311+
sw.Stop();
312+
analyser.Logger.Log(Severity.Info, " Models constructed in {0}", sw.Elapsed);
313+
var elapsed = sw.Elapsed;
369314

370-
sw.Restart();
371-
analyser.PerformExtraction(options.Threads);
372-
sw.Stop();
373-
analyser.Logger.Log(Severity.Info, " Extraction took {0}", sw.Elapsed);
315+
var currentProcess = Process.GetCurrentProcess();
316+
var cpuTime1 = currentProcess.TotalProcessorTime;
317+
var userTime1 = currentProcess.UserProcessorTime;
374318

375-
foreach (var type in analyser.MissingNamespaces)
319+
sw.Restart();
320+
analyser.PerformExtraction(options.Threads);
321+
sw.Stop();
322+
var cpuTime2 = currentProcess.TotalProcessorTime;
323+
var userTime2 = currentProcess.UserProcessorTime;
324+
325+
var performance = new Entities.PerformanceMetrics()
326+
{
327+
Frontend = new Entities.Timings() { Elapsed = elapsed, Cpu = cpuTime1, User = userTime1 },
328+
Extractor = new Entities.Timings() { Elapsed = sw.Elapsed, Cpu = cpuTime2 - cpuTime1, User = userTime2 - userTime1 },
329+
Total = new Entities.Timings() { Elapsed = stopwatch.Elapsed, Cpu = cpuTime2, User = userTime2 },
330+
PeakWorkingSet = currentProcess.PeakWorkingSet64
331+
};
332+
333+
logPerformance(performance);
334+
analyser.Logger.Log(Severity.Info, " Extraction took {0}", sw.Elapsed);
335+
336+
postProcess();
337+
338+
return analyser.TotalErrors == 0 ? ExitCode.Ok : ExitCode.Errors;
339+
}
340+
341+
private static void AnalyseStandalone(
342+
StandaloneAnalyser analyser,
343+
IEnumerable<string> sources,
344+
IEnumerable<string> referencePaths,
345+
CommonOptions options,
346+
IProgressMonitor progressMonitor,
347+
Stopwatch stopwatch)
348+
{
349+
Analyse(stopwatch, analyser, options,
350+
references => GetResolvedReferencesStandalone(referencePaths, references),
351+
(analyser, syntaxTrees) => ReadSyntaxTrees(sources, analyser, null, null, syntaxTrees),
352+
(syntaxTrees, references) => CSharpCompilation.Create("csharp.dll", syntaxTrees, references),
353+
(compilation, options) => analyser.InitializeStandalone(compilation, options),
354+
() => { },
355+
_ => { },
356+
() =>
376357
{
377-
pm.MissingNamespace(type);
378-
}
358+
foreach (var type in analyser.MissingNamespaces)
359+
{
360+
progressMonitor.MissingNamespace(type);
361+
}
362+
363+
foreach (var type in analyser.MissingTypes)
364+
{
365+
progressMonitor.MissingType(type);
366+
}
379367

380-
foreach (var type in analyser.MissingTypes)
368+
progressMonitor.MissingSummary(analyser.MissingTypes.Count(), analyser.MissingNamespaces.Count());
369+
});
370+
}
371+
372+
private static ExitCode AnalyseNonStandalone(
373+
NonStandaloneAnalyser analyser,
374+
CSharpCommandLineArguments compilerArguments,
375+
Options options,
376+
CanonicalPathCache canonicalPathCache,
377+
Stopwatch stopwatch)
378+
{
379+
return Analyse(stopwatch, analyser, options,
380+
references => ResolveReferences(compilerArguments, analyser, canonicalPathCache, references),
381+
(analyser, syntaxTrees) =>
381382
{
382-
pm.MissingType(type);
383-
}
383+
return ReadSyntaxTrees(
384+
compilerArguments.SourceFiles.Select(src => canonicalPathCache.GetCanonicalPath(src.Path)),
385+
analyser,
386+
compilerArguments.ParseOptions,
387+
compilerArguments.Encoding,
388+
syntaxTrees);
389+
},
390+
(syntaxTrees, references) =>
391+
{
392+
// csc.exe (CSharpCompiler.cs) also provides CompilationOptions
393+
// .WithMetadataReferenceResolver(),
394+
// .WithXmlReferenceResolver() and
395+
// .WithSourceReferenceResolver().
396+
// These would be needed if we hadn't explicitly provided the source/references
397+
// already.
398+
return CSharpCompilation.Create(
399+
compilerArguments.CompilationName,
400+
syntaxTrees,
401+
references,
402+
compilerArguments.CompilationOptions.
403+
WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default).
404+
WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths))
405+
);
406+
},
407+
(compilation, options) => analyser.EndInitialize(compilerArguments, options, compilation),
408+
() => analyser.AnalyseCompilation(),
409+
performance => analyser.LogPerformance(performance),
410+
() => { });
411+
}
384412

385-
pm.MissingSummary(analyser.MissingTypes.Count(), analyser.MissingNamespaces.Count());
386-
}
387-
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
413+
private static IEnumerable<Action> GetResolvedReferencesStandalone(IEnumerable<string> referencePaths, BlockingCollection<MetadataReference> references)
414+
{
415+
return referencePaths.Select<string, Action>(path => () =>
388416
{
389-
analyser.Logger.Log(Severity.Error, " Unhandled exception: {0}", ex);
390-
}
417+
var reference = MetadataReference.CreateFromFile(path);
418+
references.Add(reference);
419+
});
391420
}
392421

393422
/// <summary>

csharp/extractor/Semmle.Extraction.CSharp/Extractor/NonStandaloneAnalyser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public bool BeginInitialize(IEnumerable<string> roslynArgs)
4141
/// <returns>A Boolean indicating whether to proceed with extraction.</returns>
4242
public void EndInitialize(
4343
CSharpCommandLineArguments commandLineArguments,
44-
Options options,
44+
CommonOptions options,
4545
CSharpCompilation compilation)
4646
{
4747
if (!init)

0 commit comments

Comments
 (0)