Skip to content

Commit 6906704

Browse files
committed
Local state tracing instrumentation
1 parent 3ba398d commit 6906704

File tree

106 files changed

+8233
-425
lines changed

Some content is hidden

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

106 files changed

+8233
-425
lines changed

src/Compilers/CSharp/Portable/Binder/Binder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ internal BoundStatement WrapWithVariablesAndLocalFunctionsIfAny(CSharpSyntaxNode
882882
return statement;
883883
}
884884

885-
return new BoundBlock(statement.Syntax, locals, localFunctions, hasUnsafeModifier: false,
885+
return new BoundBlock(statement.Syntax, locals, localFunctions, hasUnsafeModifier: false, instrumentation: null,
886886
ImmutableArray.Create(statement))
887887
{ WasCompilerGenerated = true };
888888
}

src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,6 +1889,7 @@ private BoundBlock FinishBindBlockParts(CSharpSyntaxNode node, ImmutableArray<Bo
18891889
locals,
18901890
GetDeclaredLocalFunctionsForScope(node),
18911891
hasUnsafeModifier: node.Parent?.Kind() == SyntaxKind.UnsafeStatement,
1892+
instrumentation: null,
18921893
boundStatements);
18931894
}
18941895

src/Compilers/CSharp/Portable/BoundTree/BoundNode.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,26 @@ private void CheckDeclared(LocalSymbol local)
498498

499499
public override BoundNode? VisitBlock(BoundBlock node)
500500
{
501+
if (node.Instrumentation != null)
502+
{
503+
var added = DeclaredLocals.Add(node.Instrumentation.Local);
504+
Debug.Assert(added);
505+
506+
_ = Visit(node.Instrumentation.Prologue);
507+
}
508+
501509
AddAll(node.Locals);
502510
base.VisitBlock(node);
503511
RemoveAll(node.Locals);
512+
513+
if (node.Instrumentation != null)
514+
{
515+
_ = Visit(node.Instrumentation.Epilogue);
516+
517+
var removed = DeclaredLocals.Remove(node.Instrumentation.Local);
518+
Debug.Assert(removed);
519+
}
520+
504521
return null;
505522
}
506523

src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,15 +699,48 @@
699699
<Node Name="BoundTypeOfOperator" Base="BoundTypeOf">
700700
<Field Name="SourceType" Type="BoundTypeExpression"/>
701701
</Node>
702+
703+
<!-- Instrumentation info attached to BoundBlock -->
704+
<Node Name="BoundBlockInstrumentation" Base="BoundNode">
705+
<Field Name="Local" Type="LocalSymbol" Null="disallow"/>
706+
<Field Name="Prologue" Type="BoundStatement" Null="disallow"/>
707+
<Field Name="Epilogue" Type="BoundStatement" Null="disallow"/>
708+
</Node>
702709

703-
<!-- Represents the raw metadata token index value for a method definition.
710+
<!-- Represents the raw metadata RowId value for a method definition.
704711
Used by dynamic instrumentation to index into tables or arrays of per-method information. -->
705712
<Node Name="BoundMethodDefIndex" Base="BoundExpression">
706713
<!-- Non-null type is required for this node kind -->
707714
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
708715
<Field Name="Method" Type="MethodSymbol"/>
709716
</Node>
710717

718+
<!-- Represents local id - a unique int value that represents local variable.-->
719+
<Node Name="BoundLocalId" Base="BoundExpression">
720+
<!-- Non-null type is required for this node kind -->
721+
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
722+
<!-- Local symbol representing the variable. -->
723+
<Field Name="Local" Type="LocalSymbol" Null="disallow"/>
724+
<!-- Field symbol representing the variable if it has been lifted. -->
725+
<Field Name="HoistedField" Type="FieldSymbol?" Null="allow"/>
726+
</Node>
727+
728+
<!-- Represents parameter id - a unique int value that represents parameter.-->
729+
<Node Name="BoundParameterId" Base="BoundExpression">
730+
<!-- Non-null type is required for this node kind -->
731+
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
732+
<!-- Local symbol representing the variable. -->
733+
<Field Name="Parameter" Type="ParameterSymbol" Null="disallow"/>
734+
<!-- Field symbol representing the parameter if it has been lifted. -->
735+
<Field Name="HoistedField" Type="FieldSymbol?" Null="allow"/>
736+
</Node>
737+
738+
<!-- Represents state machine instance id - a unique ulong value that represents an instance of a state machine.-->
739+
<Node Name="BoundStateMachineInstanceId" Base="BoundExpression">
740+
<!-- Non-null type is required for this node kind -->
741+
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
742+
</Node>
743+
711744
<!-- Represents the maximum raw metadata token index value for any method definition in the current module. -->
712745
<Node Name="BoundMaximumMethodDefIndex" Base="BoundExpression">
713746
<!-- Non-null type is required for this node kind -->
@@ -974,6 +1007,9 @@
9741007
<Field Name="Locals" Type="ImmutableArray&lt;LocalSymbol&gt;"/>
9751008
<Field Name="LocalFunctions" Type="ImmutableArray&lt;LocalFunctionSymbol&gt;"/>
9761009
<Field Name="HasUnsafeModifier" Type="bool" Null="NotApplicable"/>
1010+
1011+
<!-- Optional block-level instrumentation info -->
1012+
<Field Name="Instrumentation" Type="BoundBlockInstrumentation?" Null="allow" />
9771013
</Node>
9781014

9791015
<!--

src/Compilers/CSharp/Portable/BoundTree/Constructors.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,8 @@ public BoundGotoStatement(SyntaxNode syntax, LabelSymbol label, bool hasErrors =
595595

596596
internal partial class BoundBlock
597597
{
598-
public BoundBlock(SyntaxNode syntax, ImmutableArray<LocalSymbol> locals, ImmutableArray<BoundStatement> statements, bool hasErrors = false) : this(syntax, locals, ImmutableArray<LocalFunctionSymbol>.Empty, hasUnsafeModifier: false, statements, hasErrors)
598+
public BoundBlock(SyntaxNode syntax, ImmutableArray<LocalSymbol> locals, ImmutableArray<BoundStatement> statements, bool hasErrors = false)
599+
: this(syntax, locals, ImmutableArray<LocalFunctionSymbol>.Empty, hasUnsafeModifier: false, instrumentation: null, statements, hasErrors)
599600
{
600601
}
601602

src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,12 +348,14 @@ private void EmitSymbolToken(TypeSymbol symbol, SyntaxNode syntaxNode)
348348

349349
private void EmitSymbolToken(MethodSymbol method, SyntaxNode syntaxNode, BoundArgListOperator optArgList, bool encodeAsRawDefinitionToken = false)
350350
{
351-
_builder.EmitToken(_module.Translate(method, syntaxNode, _diagnostics.DiagnosticBag, optArgList, needDeclaration: encodeAsRawDefinitionToken), syntaxNode, _diagnostics.DiagnosticBag, encodeAsRawDefinitionToken);
351+
var methodRef = _module.Translate(method, syntaxNode, _diagnostics.DiagnosticBag, optArgList, needDeclaration: encodeAsRawDefinitionToken);
352+
_builder.EmitToken(methodRef, syntaxNode, _diagnostics.DiagnosticBag, encodeAsRawDefinitionToken ? Cci.MetadataWriter.RawTokenEncoding.RowId : 0);
352353
}
353354

354355
private void EmitSymbolToken(FieldSymbol symbol, SyntaxNode syntaxNode)
355356
{
356-
_builder.EmitToken(_module.Translate(symbol, syntaxNode, _diagnostics.DiagnosticBag), syntaxNode, _diagnostics.DiagnosticBag);
357+
var fieldRef = _module.Translate(symbol, syntaxNode, _diagnostics.DiagnosticBag);
358+
_builder.EmitToken(fieldRef, syntaxNode, _diagnostics.DiagnosticBag);
357359
}
358360

359361
private void EmitSignatureToken(FunctionPointerTypeSymbol symbol, SyntaxNode syntaxNode)

src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,16 @@ private void EmitExpressionCore(BoundExpression expression, bool used)
257257
EmitSourceDocumentIndex((BoundSourceDocumentIndex)expression);
258258
break;
259259

260+
case BoundKind.LocalId:
261+
Debug.Assert(used);
262+
EmitLocalIdExpression((BoundLocalId)expression);
263+
break;
264+
265+
case BoundKind.ParameterId:
266+
Debug.Assert(used);
267+
EmitParameterIdExpression((BoundParameterId)expression);
268+
break;
269+
260270
case BoundKind.MethodInfo:
261271
if (used)
262272
{
@@ -3310,6 +3320,43 @@ private void EmitMethodDefIndexExpression(BoundMethodDefIndex node)
33103320
EmitSymbolToken(symbol, node.Syntax, null, encodeAsRawDefinitionToken: true);
33113321
}
33123322

3323+
private void EmitLocalIdExpression(BoundLocalId node)
3324+
{
3325+
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);
3326+
3327+
if (node.HoistedField is null)
3328+
{
3329+
_builder.EmitIntConstant(GetLocal(node.Local).SlotIndex);
3330+
}
3331+
else
3332+
{
3333+
EmitHoistedVariableId(node.HoistedField, node.Syntax);
3334+
}
3335+
}
3336+
3337+
private void EmitParameterIdExpression(BoundParameterId node)
3338+
{
3339+
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);
3340+
3341+
if (node.HoistedField is null)
3342+
{
3343+
_builder.EmitIntConstant(node.Parameter.Ordinal);
3344+
}
3345+
else
3346+
{
3347+
EmitHoistedVariableId(node.HoistedField, node.Syntax);
3348+
}
3349+
}
3350+
3351+
private void EmitHoistedVariableId(FieldSymbol field, SyntaxNode syntax)
3352+
{
3353+
Debug.Assert(field.IsDefinition);
3354+
var fieldRef = _module.Translate(field, syntax, _diagnostics.DiagnosticBag, needDeclaration: true);
3355+
3356+
_builder.EmitOpCode(ILOpCode.Ldtoken);
3357+
_builder.EmitToken(fieldRef, syntax, _diagnostics.DiagnosticBag, Cci.MetadataWriter.RawTokenEncoding.LiftedVariableId);
3358+
}
3359+
33133360
private void EmitMaximumMethodDefIndexExpression(BoundMaximumMethodDefIndex node)
33143361
{
33153362
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);

src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,54 @@ private bool IsLastBlockInMethod(BoundBlock block)
622622
}
623623

624624
private void EmitBlock(BoundBlock block)
625+
{
626+
if (block.Instrumentation is not null)
627+
{
628+
EmitInstrumentedBlock(block.Instrumentation, block);
629+
}
630+
else
631+
{
632+
EmitUninstrumentedBlock(block);
633+
}
634+
}
635+
636+
private void EmitInstrumentedBlock(BoundBlockInstrumentation instrumentation, BoundBlock block)
637+
{
638+
_builder.OpenLocalScope();
639+
DefineLocal(instrumentation.Local, block.Syntax);
640+
641+
if (_emitPdbSequencePoints)
642+
{
643+
EmitHiddenSequencePoint();
644+
}
645+
646+
EmitStatement(instrumentation.Prologue);
647+
648+
_builder.AssertStackEmpty();
649+
650+
_builder.OpenLocalScope(ScopeType.TryCatchFinally);
651+
652+
_builder.OpenLocalScope(ScopeType.Try);
653+
EmitUninstrumentedBlock(block);
654+
_builder.CloseLocalScope(); // try
655+
656+
_builder.OpenLocalScope(ScopeType.Finally);
657+
658+
if (_emitPdbSequencePoints)
659+
{
660+
EmitHiddenSequencePoint();
661+
}
662+
663+
EmitStatement(instrumentation.Epilogue);
664+
_builder.CloseLocalScope(); // finally
665+
666+
_builder.CloseLocalScope(); // try-finally
667+
668+
FreeLocal(instrumentation.Local);
669+
_builder.CloseLocalScope();
670+
}
671+
672+
private void EmitUninstrumentedBlock(BoundBlock block)
625673
{
626674
var hasLocals = !block.Locals.IsEmpty;
627675

@@ -644,7 +692,15 @@ private void EmitBlock(BoundBlock block)
644692
if (_indirectReturnState == IndirectReturnState.Needed &&
645693
IsLastBlockInMethod(block))
646694
{
647-
HandleReturn();
695+
if (block.Instrumentation != null)
696+
{
697+
// jump out of try-finally
698+
_builder.EmitBranch(ILOpCode.Br, s_returnLabel);
699+
}
700+
else
701+
{
702+
HandleReturn();
703+
}
648704
}
649705

650706
if (hasLocals)

src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,11 @@ public override BoundNode VisitBlock(BoundBlock node)
614614
{
615615
Debug.Assert(EvalStackIsEmpty(), "entering blocks when evaluation stack is not empty?");
616616

617+
if (node.Instrumentation != null)
618+
{
619+
DeclareLocal(node.Instrumentation.Local, stack: 0);
620+
}
621+
617622
// normally we would not allow stack locals
618623
// when evaluation stack is not empty.
619624
DeclareLocals(node.Locals, 0);

src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2791,7 +2791,6 @@ private void GetDiagnosticsForAllMethodBodies(BindingDiagnosticBag diagnostics,
27912791
cancellationToken: cancellationToken)
27922792
: null,
27932793
emittingPdb: false,
2794-
emitTestCoverageData: false,
27952794
hasDeclarationErrors: false,
27962795
emitMethodBodies: false,
27972796
diagnostics: diagnostics,
@@ -2905,7 +2904,6 @@ void compileMethodBodiesAndDocComments(SyntaxTree? filterTree, TextSpan? filterS
29052904
compilation: this,
29062905
moduleBeingBuiltOpt: null,
29072906
emittingPdb: false,
2908-
emitTestCoverageData: false,
29092907
hasDeclarationErrors: false,
29102908
emitMethodBodies: false,
29112909
diagnostics: bindingDiagnostics,
@@ -3191,12 +3189,12 @@ internal override StrongNameKeys StrongNameKeys
31913189
internal override bool CompileMethods(
31923190
CommonPEModuleBuilder moduleBuilder,
31933191
bool emittingPdb,
3194-
bool emitMetadataOnly,
3195-
bool emitTestCoverageData,
31963192
DiagnosticBag diagnostics,
31973193
Predicate<ISymbolInternal>? filterOpt,
31983194
CancellationToken cancellationToken)
31993195
{
3196+
var emitMetadataOnly = moduleBuilder.EmitOptions.EmitMetadataOnly;
3197+
32003198
// The diagnostics should include syntax and declaration errors. We insert these before calling Emitter.Emit, so that the emitter
32013199
// does not attempt to emit if there are declaration errors (but we do insert all errors from method body binding...)
32023200
PooledHashSet<int>? excludeDiagnostics = null;
@@ -3233,7 +3231,7 @@ internal override bool CompileMethods(
32333231
}
32343232
else
32353233
{
3236-
if ((emittingPdb || emitTestCoverageData) &&
3234+
if ((emittingPdb || moduleBeingBuilt.EmitOptions.InstrumentationKinds.Contains(InstrumentationKind.TestCoverage)) &&
32373235
!CreateDebugDocuments(moduleBeingBuilt.DebugDocumentsBuilder, moduleBeingBuilt.EmbeddedTexts, diagnostics))
32383236
{
32393237
return false;
@@ -3251,7 +3249,6 @@ internal override bool CompileMethods(
32513249
this,
32523250
moduleBeingBuilt,
32533251
emittingPdb,
3254-
emitTestCoverageData,
32553252
hasDeclarationErrors,
32563253
emitMethodBodies: true,
32573254
diagnostics: new BindingDiagnosticBag(methodBodyDiagnosticBag),

0 commit comments

Comments
 (0)