Skip to content

Commit 0096024

Browse files
authored
Merge pull request #1174 from calumgrant/cs/extractor-diagnostics
C#: Log compiler and extractor diagnostics
2 parents bb67ac9 + 88b5719 commit 0096024

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+7653
-145
lines changed

csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using Semmle.Util.Logging;
55
using System;
6+
using Semmle.Extraction.Entities;
67

78
namespace Semmle.Extraction.CIL.Entities
89
{
@@ -70,12 +71,7 @@ public override IEnumerable<IExtractionProduct> Contents
7071
}
7172
catch (InternalError e)
7273
{
73-
cx.cx.Extractor.Message(new Message
74-
{
75-
exception = e,
76-
message = "Error processing type definition",
77-
severity = Semmle.Util.Logging.Severity.Error
78-
});
74+
cx.cx.ExtractionError("Error processing type definition", e.Message, GeneratedLocation.Create(cx.cx), e.StackTrace);
7975
}
8076

8177
// Limitation of C#: Cannot yield return inside a try-catch.
@@ -92,12 +88,7 @@ public override IEnumerable<IExtractionProduct> Contents
9288
}
9389
catch (InternalError e)
9490
{
95-
cx.cx.Extractor.Message(new Message
96-
{
97-
exception = e,
98-
message = "Error processing bytecode",
99-
severity = Semmle.Util.Logging.Severity.Error
100-
});
91+
cx.cx.ExtractionError("Error processing bytecode", e.Message, GeneratedLocation.Create(cx.cx), e.StackTrace);
10192
}
10293

10394
if (product != null)
@@ -139,7 +130,7 @@ public static void ExtractCIL(Layout layout, string assemblyPath, ILogger logger
139130
trapFile = trapWriter.TrapFile;
140131
if (nocache || !System.IO.File.Exists(trapFile))
141132
{
142-
var cx = new Extraction.Context(extractor, null, trapWriter, null);
133+
var cx = extractor.CreateContext(null, trapWriter, null);
143134
ExtractCIL(cx, assemblyPath, extractPdbs);
144135
extracted = true;
145136
}

csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ public override IEnumerable<IExtractionProduct> Contents
409409
}
410410
else
411411
{
412-
throw new InternalError("Unable to create payload type {0} for opcode {1}", PayloadType, OpCode);
412+
throw new InternalError($"Unable to create payload type {PayloadType} for opcode {OpCode}");
413413
}
414414
break;
415415
case Payload.Arg8:
@@ -430,7 +430,7 @@ public override IEnumerable<IExtractionProduct> Contents
430430
// Some of these are handled by JumpContents().
431431
break;
432432
default:
433-
throw new InternalError("Unhandled payload type {0}", PayloadType);
433+
throw new InternalError($"Unhandled payload type {PayloadType}");
434434
}
435435
}
436436
}
@@ -479,7 +479,7 @@ public IEnumerable<IExtractionProduct> JumpContents(Dictionary<int, IInstruction
479479
// TODO: Find a solution to this.
480480

481481
// For now, just log the error
482-
cx.cx.Extractor.Message(new Message { message = "A CIL instruction jumps outside the current method", severity = Util.Logging.Severity.Warning });
482+
cx.cx.ExtractionError("A CIL instruction jumps outside the current method", "", Extraction.Entities.GeneratedLocation.Create(cx.cx), "", Util.Logging.Severity.Warning);
483483
}
484484
}
485485
}

csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ public override IEnumerable<IExtractionProduct> Contents
497497
constructedTypeSignature = md.DecodeSignature(cx.TypeSignatureDecoder, this);
498498
break;
499499
default:
500-
throw new InternalError("Unexpected constructed method handle kind {0}", ms.Method.Kind);
500+
throw new InternalError($"Unexpected constructed method handle kind {ms.Method.Kind}");
501501
}
502502

503503
PopulateParameters(constructedTypeSignature.ParameterTypes);

csharp/extractor/Semmle.Extraction.CIL/Id.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public static StringId Id(this PrimitiveTypeCode typeCode)
197197
case PrimitiveTypeCode.UIntPtr: return uintptrId;
198198
case PrimitiveTypeCode.Void: return voidId;
199199
case PrimitiveTypeCode.TypedReference: return typedReferenceId;
200-
default: throw new InternalError("Unhandled type code {0}", typeCode);
200+
default: throw new InternalError($"Unhandled type code {typeCode}");
201201
}
202202
}
203203
}

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

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,7 @@ void SetReferencePaths()
9999
}
100100
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
101101
{
102-
extractor.Message(new Message
103-
{
104-
exception = ex,
105-
message = string.Format("Exception reading reference file {0}: {1}",
106-
reference.FilePath, ex)
107-
});
102+
extractor.Message(new Message("Exception reading reference file", reference.FilePath, null, ex.StackTrace));
108103
}
109104
}
110105
}
@@ -215,6 +210,38 @@ bool FileIsCached(string src, string dest)
215210
return options.Cache && FileIsUpToDate(src, dest);
216211
}
217212

213+
/// <summary>
214+
/// Extracts compilation-wide entities, such as compilations and compiler diagnostics.
215+
/// </summary>
216+
public void AnalyseCompilation(string cwd, string[] args)
217+
{
218+
extractionTasks.Add(() => DoAnalyseCompilation(cwd, args));
219+
}
220+
221+
Entities.Compilation compilationEntity;
222+
IDisposable compilationTrapFile;
223+
224+
void DoAnalyseCompilation(string cwd, string[] args)
225+
{
226+
try
227+
{
228+
var assemblyPath = extractor.OutputPath;
229+
var assembly = compilation.Assembly;
230+
var projectLayout = layout.LookupProjectOrDefault(assemblyPath);
231+
var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true);
232+
compilationTrapFile = trapWriter; // Dispose later
233+
var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath, true));
234+
235+
compilationEntity = new Entities.Compilation(cx, cwd, args);
236+
}
237+
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
238+
{
239+
Logger.Log(Severity.Error, " Unhandled exception analyzing {0}: {1}", "compilation", ex);
240+
}
241+
}
242+
243+
public void LogPerformance(Entities.PerformanceMetrics p) => compilationEntity.PopulatePerformance(p);
244+
218245
/// <summary>
219246
/// Extract an assembly to a new trap file.
220247
/// If the trap file exists, skip extraction to avoid duplicating
@@ -258,7 +285,7 @@ void DoAnalyseAssembly(PortableExecutableReference r)
258285

259286
if (assembly != null)
260287
{
261-
var cx = new Context(extractor, c, trapWriter, new AssemblyScope(assembly, assemblyPath));
288+
var cx = extractor.CreateContext(c, trapWriter, new AssemblyScope(assembly, assemblyPath, false));
262289

263290
foreach (var module in assembly.Modules)
264291
{
@@ -344,7 +371,7 @@ void DoExtractTree(SyntaxTree tree)
344371

345372
if (!upToDate)
346373
{
347-
Context cx = new Context(extractor, compilation.Clone(), trapWriter, new SourceScope(tree));
374+
Context cx = extractor.CreateContext(compilation.Clone(), trapWriter, new SourceScope(tree));
348375
Populators.CompilationUnit.Extract(cx, tree.GetRoot());
349376
cx.PopulateAll();
350377
cx.ExtractComments(cx.CommentGenerator);
@@ -356,7 +383,7 @@ void DoExtractTree(SyntaxTree tree)
356383
}
357384
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
358385
{
359-
extractor.Message(new Message { exception = ex, message = string.Format("Unhandled exception processing {0}: {1}", tree.FilePath, ex), severity = Severity.Error });
386+
extractor.Message(new Message("Unhandled exception processing syntax tree", tree.FilePath, null, ex.StackTrace));
360387
}
361388
}
362389

@@ -373,6 +400,8 @@ public void PerformExtraction(int numberOfThreads)
373400

374401
public void Dispose()
375402
{
403+
compilationTrapFile?.Dispose();
404+
376405
stopWatch.Stop();
377406
Logger.Log(Severity.Info, " Peak working set = {0} MB", Process.GetCurrentProcess().PeakWorkingSet64 / (1024 * 1024));
378407

csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ So we split it into separate lines
105105
break;
106106
// Strangely, these are reported as SingleLineCommentTrivia.
107107
case SyntaxKind.DocumentationCommentExteriorTrivia:
108-
cx.ModelError("Unhandled comment type {0} for {1}", trivia.Kind(), trivia);
108+
cx.ModelError($"Unhandled comment type {trivia.Kind()} for {trivia}");
109109
break;
110110
}
111111
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using Microsoft.CodeAnalysis;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Diagnostics;
5+
using System.Linq;
6+
using System.Text;
7+
8+
namespace Semmle.Extraction.CSharp.Entities
9+
{
10+
class Compilation : FreshEntity
11+
{
12+
public Compilation(Context cx, string cwd, string[] args) : base(cx)
13+
{
14+
Extraction.Entities.Assembly.CreateOutputAssembly(cx);
15+
16+
cx.Emit(Tuples.compilations(this, Extraction.Entities.File.PathAsDatabaseString(cwd)));
17+
18+
// Arguments
19+
int index = 0;
20+
foreach(var arg in args)
21+
{
22+
cx.Emit(Tuples.compilation_args(this, index++, arg));
23+
}
24+
25+
// Files
26+
index = 0;
27+
foreach(var file in cx.Compilation.SyntaxTrees.Select(tree => Extraction.Entities.File.Create(cx, tree.FilePath)))
28+
{
29+
cx.Emit(Tuples.compilation_compiling_files(this, index++, file));
30+
}
31+
32+
// References
33+
index = 0;
34+
foreach(var file in cx.Compilation.References.OfType<PortableExecutableReference>().Select(r => Extraction.Entities.File.Create(cx, r.FilePath)))
35+
{
36+
cx.Emit(Tuples.compilation_referencing_files(this, index++, file));
37+
}
38+
39+
// Diagnostics
40+
index = 0;
41+
foreach(var diag in cx.Compilation.GetDiagnostics().Select(d => new Diagnostic(cx, d)))
42+
{
43+
cx.Emit(Tuples.diagnostic_for(diag, this, 0, index++));
44+
}
45+
}
46+
47+
public void PopulatePerformance(PerformanceMetrics p)
48+
{
49+
int index = 0;
50+
foreach(float metric in p.Metrics)
51+
{
52+
cx.Emit(Tuples.compilation_time(this, -1, index++, metric));
53+
}
54+
cx.Emit(Tuples.compilation_finished(this, (float)p.Total.Cpu.TotalSeconds, (float)p.Total.Elapsed.TotalSeconds));
55+
}
56+
57+
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;
58+
}
59+
60+
class Diagnostic : FreshEntity
61+
{
62+
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;
63+
64+
public Diagnostic(Context cx, Microsoft.CodeAnalysis.Diagnostic diag) : base(cx)
65+
{
66+
cx.Emit(Tuples.diagnostics(this, (int)diag.Severity, diag.Id, diag.Descriptor.Title.ToString(),
67+
diag.GetMessage(), Extraction.Entities.Location.Create(cx, diag.Location)));
68+
}
69+
}
70+
71+
public struct Timings
72+
{
73+
public TimeSpan Elapsed, Cpu, User;
74+
}
75+
76+
/// <summary>
77+
/// The various performance metrics to log.
78+
/// </summary>
79+
public struct PerformanceMetrics
80+
{
81+
public Timings Frontend, Extractor, Total;
82+
public long PeakWorkingSet;
83+
84+
/// <summary>
85+
/// These are in database order (0 indexed)
86+
/// </summary>
87+
public IEnumerable<float> Metrics
88+
{
89+
get
90+
{
91+
yield return (float)Frontend.Cpu.TotalSeconds;
92+
yield return (float)Frontend.Elapsed.TotalSeconds;
93+
yield return (float)Extractor.Cpu.TotalSeconds;
94+
yield return (float)Extractor.Elapsed.TotalSeconds;
95+
yield return (float)Frontend.User.TotalSeconds;
96+
yield return (float)Extractor.User.TotalSeconds;
97+
yield return PeakWorkingSet / 1024.0f / 1024.0f;
98+
}
99+
}
100+
}
101+
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Access.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static ExprKind AccessKind(Context cx, ISymbol symbol)
3434
return ExprKind.PARAMETER_ACCESS;
3535

3636
default:
37-
cx.ModelError(symbol, "Unhandled access kind '{0}'", symbol.Kind);
37+
cx.ModelError(symbol, $"Unhandled access kind '{symbol.Kind}'");
3838
return ExprKind.UNKNOWN;
3939
}
4040
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ static ExprKind GetBinaryTokenKind(Context cx, SyntaxKind kind)
5353
case SyntaxKind.QuestionQuestionToken: return ExprKind.NULL_COALESCING;
5454
// !! And the rest
5555
default:
56-
cx.ModelError("Unhandled operator type {0}", kind);
56+
cx.ModelError($"Unhandled operator type {kind}");
5757
return ExprKind.UNKNOWN;
5858
}
5959
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ internal static Expression Create(ExpressionNodeInfo info)
233233
return IsPattern.Create(info);
234234

235235
default:
236-
info.Context.ModelError(info.Node, "Unhandled expression '{0}' of kind '{1}'", info.Node, info.Node.Kind());
236+
info.Context.ModelError(info.Node, $"Unhandled expression '{info.Node}' of kind '{info.Node.Kind()}'");
237237
return new Unknown(info);
238238
}
239239
}

0 commit comments

Comments
 (0)