Skip to content

Commit

Permalink
Local state tracing instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat committed Feb 9, 2023
1 parent 3ba398d commit 680a9ac
Show file tree
Hide file tree
Showing 106 changed files with 8,231 additions and 425 deletions.
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ internal BoundStatement WrapWithVariablesAndLocalFunctionsIfAny(CSharpSyntaxNode
return statement;
}

return new BoundBlock(statement.Syntax, locals, localFunctions, hasUnsafeModifier: false,
return new BoundBlock(statement.Syntax, locals, localFunctions, hasUnsafeModifier: false, instrumentation: null,
ImmutableArray.Create(statement))
{ WasCompilerGenerated = true };
}
Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1889,6 +1889,7 @@ private BoundBlock FinishBindBlockParts(CSharpSyntaxNode node, ImmutableArray<Bo
locals,
GetDeclaredLocalFunctionsForScope(node),
hasUnsafeModifier: node.Parent?.Kind() == SyntaxKind.UnsafeStatement,
instrumentation: null,
boundStatements);
}

Expand Down
17 changes: 17 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,26 @@ private void CheckDeclared(LocalSymbol local)

public override BoundNode? VisitBlock(BoundBlock node)
{
if (node.Instrumentation != null)
{
var added = DeclaredLocals.Add(node.Instrumentation.Local);
Debug.Assert(added);

_ = Visit(node.Instrumentation.Prologue);
}

AddAll(node.Locals);
base.VisitBlock(node);
RemoveAll(node.Locals);

if (node.Instrumentation != null)
{
_ = Visit(node.Instrumentation.Epilogue);

var removed = DeclaredLocals.Remove(node.Instrumentation.Local);
Debug.Assert(removed);
}

return null;
}

Expand Down
38 changes: 37 additions & 1 deletion src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -699,15 +699,48 @@
<Node Name="BoundTypeOfOperator" Base="BoundTypeOf">
<Field Name="SourceType" Type="BoundTypeExpression"/>
</Node>

<!-- Instrumentation info attached to BoundBlock -->
<Node Name="BoundBlockInstrumentation" Base="BoundNode">
<Field Name="Local" Type="LocalSymbol" Null="disallow"/>
<Field Name="Prologue" Type="BoundStatement" Null="disallow"/>
<Field Name="Epilogue" Type="BoundStatement" Null="disallow"/>
</Node>

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

<!-- Represents local id - a unique int value that represents local variable.-->
<Node Name="BoundLocalId" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<!-- Local symbol representing the variable. -->
<Field Name="Local" Type="LocalSymbol" Null="disallow"/>
<!-- Field symbol representing the variable if it has been lifted. -->
<Field Name="HoistedField" Type="FieldSymbol?" Null="allow"/>
</Node>

<!-- Represents parameter id - a unique int value that represents parameter.-->
<Node Name="BoundParameterId" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<!-- Local symbol representing the variable. -->
<Field Name="Parameter" Type="ParameterSymbol" Null="disallow"/>
<!-- Field symbol representing the parameter if it has been lifted. -->
<Field Name="HoistedField" Type="FieldSymbol?" Null="allow"/>
</Node>

<!-- Represents state machine instance id - a unique ulong value that represents an instance of a state machine.-->
<Node Name="BoundStateMachineInstanceId" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
</Node>

<!-- Represents the maximum raw metadata token index value for any method definition in the current module. -->
<Node Name="BoundMaximumMethodDefIndex" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
Expand Down Expand Up @@ -974,6 +1007,9 @@
<Field Name="Locals" Type="ImmutableArray&lt;LocalSymbol&gt;"/>
<Field Name="LocalFunctions" Type="ImmutableArray&lt;LocalFunctionSymbol&gt;"/>
<Field Name="HasUnsafeModifier" Type="bool" Null="NotApplicable"/>

<!-- Optional block-level instrumentation info -->
<Field Name="Instrumentation" Type="BoundBlockInstrumentation?" Null="allow" />
</Node>

<!--
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/BoundTree/Constructors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,8 @@ public BoundGotoStatement(SyntaxNode syntax, LabelSymbol label, bool hasErrors =

internal partial class BoundBlock
{
public BoundBlock(SyntaxNode syntax, ImmutableArray<LocalSymbol> locals, ImmutableArray<BoundStatement> statements, bool hasErrors = false) : this(syntax, locals, ImmutableArray<LocalFunctionSymbol>.Empty, hasUnsafeModifier: false, statements, hasErrors)
public BoundBlock(SyntaxNode syntax, ImmutableArray<LocalSymbol> locals, ImmutableArray<BoundStatement> statements, bool hasErrors = false)
: this(syntax, locals, ImmutableArray<LocalFunctionSymbol>.Empty, hasUnsafeModifier: false, instrumentation: null, statements, hasErrors)
{
}

Expand Down
6 changes: 4 additions & 2 deletions src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,14 @@ private void EmitSymbolToken(TypeSymbol symbol, SyntaxNode syntaxNode)

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

private void EmitSymbolToken(FieldSymbol symbol, SyntaxNode syntaxNode)
{
_builder.EmitToken(_module.Translate(symbol, syntaxNode, _diagnostics.DiagnosticBag), syntaxNode, _diagnostics.DiagnosticBag);
var fieldRef = _module.Translate(symbol, syntaxNode, _diagnostics.DiagnosticBag);
_builder.EmitToken(fieldRef, syntaxNode, _diagnostics.DiagnosticBag);
}

private void EmitSignatureToken(FunctionPointerTypeSymbol symbol, SyntaxNode syntaxNode)
Expand Down
47 changes: 47 additions & 0 deletions src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,16 @@ private void EmitExpressionCore(BoundExpression expression, bool used)
EmitSourceDocumentIndex((BoundSourceDocumentIndex)expression);
break;

case BoundKind.LocalId:
Debug.Assert(used);
EmitLocalIdExpression((BoundLocalId)expression);
break;

case BoundKind.ParameterId:
Debug.Assert(used);
EmitParameterIdExpression((BoundParameterId)expression);
break;

case BoundKind.MethodInfo:
if (used)
{
Expand Down Expand Up @@ -3310,6 +3320,43 @@ private void EmitMethodDefIndexExpression(BoundMethodDefIndex node)
EmitSymbolToken(symbol, node.Syntax, null, encodeAsRawDefinitionToken: true);
}

private void EmitLocalIdExpression(BoundLocalId node)
{
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);

if (node.HoistedField is null)
{
_builder.EmitIntConstant(GetLocal(node.Local).SlotIndex);
}
else
{
EmitHoistedVariableId(node.HoistedField, node.Syntax);
}
}

private void EmitParameterIdExpression(BoundParameterId node)
{
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);

if (node.HoistedField is null)
{
_builder.EmitIntConstant(node.Parameter.Ordinal);
}
else
{
EmitHoistedVariableId(node.HoistedField, node.Syntax);
}
}

private void EmitHoistedVariableId(FieldSymbol field, SyntaxNode syntax)
{
Debug.Assert(field.IsDefinition);
var fieldRef = _module.Translate(field, syntax, _diagnostics.DiagnosticBag, needDeclaration: true);

_builder.EmitOpCode(ILOpCode.Ldtoken);
_builder.EmitToken(fieldRef, syntax, _diagnostics.DiagnosticBag, Cci.MetadataWriter.RawTokenEncoding.LiftedVariableId);
}

private void EmitMaximumMethodDefIndexExpression(BoundMaximumMethodDefIndex node)
{
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);
Expand Down
58 changes: 57 additions & 1 deletion src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,54 @@ private bool IsLastBlockInMethod(BoundBlock block)
}

private void EmitBlock(BoundBlock block)
{
if (block.Instrumentation is not null)
{
EmitInstrumentedBlock(block.Instrumentation, block);
}
else
{
EmitUninstrumentedBlock(block);
}
}

private void EmitInstrumentedBlock(BoundBlockInstrumentation instrumentation, BoundBlock block)
{
_builder.OpenLocalScope();
DefineLocal(instrumentation.Local, block.Syntax);

if (_emitPdbSequencePoints)
{
EmitHiddenSequencePoint();
}

EmitStatement(instrumentation.Prologue);

_builder.AssertStackEmpty();

_builder.OpenLocalScope(ScopeType.TryCatchFinally);

_builder.OpenLocalScope(ScopeType.Try);
EmitUninstrumentedBlock(block);
_builder.CloseLocalScope(); // try

_builder.OpenLocalScope(ScopeType.Finally);

if (_emitPdbSequencePoints)
{
EmitHiddenSequencePoint();
}

EmitStatement(instrumentation.Epilogue);
_builder.CloseLocalScope(); // finally

_builder.CloseLocalScope(); // try-finally

FreeLocal(instrumentation.Local);
_builder.CloseLocalScope();
}

private void EmitUninstrumentedBlock(BoundBlock block)
{
var hasLocals = !block.Locals.IsEmpty;

Expand All @@ -644,7 +692,15 @@ private void EmitBlock(BoundBlock block)
if (_indirectReturnState == IndirectReturnState.Needed &&
IsLastBlockInMethod(block))
{
HandleReturn();
if (block.Instrumentation != null)
{
// jump out of try-finally
_builder.EmitBranch(ILOpCode.Br, s_returnLabel);
}
else
{
HandleReturn();
}
}

if (hasLocals)
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,11 @@ public override BoundNode VisitBlock(BoundBlock node)
{
Debug.Assert(EvalStackIsEmpty(), "entering blocks when evaluation stack is not empty?");

if (node.Instrumentation != null)
{
DeclareLocal(node.Instrumentation.Local, stack: 0);
}

// normally we would not allow stack locals
// when evaluation stack is not empty.
DeclareLocals(node.Locals, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2791,7 +2791,6 @@ private void GetDiagnosticsForAllMethodBodies(BindingDiagnosticBag diagnostics,
cancellationToken: cancellationToken)
: null,
emittingPdb: false,
emitTestCoverageData: false,
hasDeclarationErrors: false,
emitMethodBodies: false,
diagnostics: diagnostics,
Expand Down Expand Up @@ -2905,7 +2904,6 @@ void compileMethodBodiesAndDocComments(SyntaxTree? filterTree, TextSpan? filterS
compilation: this,
moduleBeingBuiltOpt: null,
emittingPdb: false,
emitTestCoverageData: false,
hasDeclarationErrors: false,
emitMethodBodies: false,
diagnostics: bindingDiagnostics,
Expand Down Expand Up @@ -3191,12 +3189,12 @@ internal override StrongNameKeys StrongNameKeys
internal override bool CompileMethods(
CommonPEModuleBuilder moduleBuilder,
bool emittingPdb,
bool emitMetadataOnly,
bool emitTestCoverageData,
DiagnosticBag diagnostics,
Predicate<ISymbolInternal>? filterOpt,
CancellationToken cancellationToken)
{
var emitMetadataOnly = moduleBuilder.EmitOptions.EmitMetadataOnly;

// The diagnostics should include syntax and declaration errors. We insert these before calling Emitter.Emit, so that the emitter
// does not attempt to emit if there are declaration errors (but we do insert all errors from method body binding...)
PooledHashSet<int>? excludeDiagnostics = null;
Expand Down Expand Up @@ -3233,7 +3231,7 @@ internal override bool CompileMethods(
}
else
{
if ((emittingPdb || emitTestCoverageData) &&
if ((emittingPdb || moduleBeingBuilt.EmitOptions.InstrumentationKinds.Contains(InstrumentationKind.TestCoverage)) &&
!CreateDebugDocuments(moduleBeingBuilt.DebugDocumentsBuilder, moduleBeingBuilt.EmbeddedTexts, diagnostics))
{
return false;
Expand All @@ -3251,7 +3249,6 @@ internal override bool CompileMethods(
this,
moduleBeingBuilt,
emittingPdb,
emitTestCoverageData,
hasDeclarationErrors,
emitMethodBodies: true,
diagnostics: new BindingDiagnosticBag(methodBodyDiagnosticBag),
Expand Down
Loading

0 comments on commit 680a9ac

Please sign in to comment.