예제 #1
0
        static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context, ref int?ctorCallStart)
        {
            bool modified     = false;
            var  instructions = block.Instructions;

            for (int i = 0; i < instructions.Count;)
            {
                if (instructions[i] is StLoc inst)
                {
                    InliningOptions options = InliningOptions.None;
                    if (IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst, ref ctorCallStart))
                    {
                        options = InliningOptions.Aggressive;
                    }
                    if (InlineOneIfPossible(block, i, options, context))
                    {
                        modified = true;
                        i        = Math.Max(0, i - 1);
                        // Go back one step
                        continue;
                    }
                }
                i++;
            }
            return(modified);
        }
예제 #2
0
파일: ILInlining.cs 프로젝트: quitec/ilspy
        /// <summary>
        /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible.
        /// </summary>
        public static bool InlineOneIfPossible(Block block, int pos, InliningOptions options, ILTransformContext context)
        {
            context.CancellationToken.ThrowIfCancellationRequested();
            StLoc stloc = block.Instructions[pos] as StLoc;

            if (stloc == null || stloc.Variable.Kind == VariableKind.PinnedLocal)
            {
                return(false);
            }
            ILVariable v = stloc.Variable;

            // ensure the variable is accessed only a single time
            if (v.StoreCount != 1)
            {
                return(false);
            }
            if (v.LoadCount > 1 || v.LoadCount + v.AddressCount != 1)
            {
                return(false);
            }
            // TODO: inlining of small integer types might be semantically incorrect,
            // but we can't avoid it this easily without breaking lots of tests.
            //if (v.Type.IsSmallIntegerType())
            //	return false; // stloc might perform implicit truncation
            return(InlineOne(stloc, options, context));
        }
        public void TracingDisabled_DoesNotSubmitsTraces(InliningOptions inlining, InstrumentationOptions instrumentation, bool enableSocketsHandler)
        {
            ConfigureInstrumentation(inlining, instrumentation, enableSocketsHandler);

            const string expectedOperationName = "http.request";

            int agentPort = TcpPortProvider.GetOpenPort();
            int httpPort  = TcpPortProvider.GetOpenPort();

            using (var agent = new MockTracerAgent(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, arguments: $"TracingDisabled Port={httpPort}"))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(1, 2000, operationName: expectedOperationName);
                    Assert.Equal(0, spans.Count);

                    var traceId        = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.TraceId);
                    var parentSpanId   = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.ParentId);
                    var tracingEnabled = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.TracingEnabled);

                    Assert.Null(traceId);
                    Assert.Null(parentSpanId);
                    Assert.Equal("false", tracingEnabled);
                }
        }
예제 #4
0
        internal static InliningOptions OptionsForBlock(Block block)
        {
            InliningOptions options = InliningOptions.None;

            if (IsCatchWhenBlock(block))
            {
                options |= InliningOptions.Aggressive;
            }
            return(options);
        }
        public void HttpClient_SubmitsTraces(InliningOptions inlining, InstrumentationOptions instrumentation, bool enableSocketsHandler)
        {
            ConfigureInstrumentation(inlining, instrumentation, enableSocketsHandler);

            var expectedAsyncCount = CalculateExpectedAsyncSpans(instrumentation, inlining.EnableCallTarget);
            var expectedSyncCount  = CalculateExpectedSyncSpans(instrumentation);

            var expectedSpanCount = expectedAsyncCount + expectedSyncCount;

            const string expectedOperationName = "http.request";
            const string expectedServiceName   = "Samples.HttpMessageHandler-http-client";

            int agentPort = TcpPortProvider.GetOpenPort();
            int httpPort  = TcpPortProvider.GetOpenPort();

            Output.WriteLine($"Assigning port {agentPort} for the agentPort.");
            Output.WriteLine($"Assigning port {httpPort} for the httpPort.");

            using (var agent = new MockTracerAgent(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, arguments: $"Port={httpPort}"))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(expectedSpanCount, operationName: expectedOperationName);
                    Assert.Equal(expectedSpanCount, spans.Count);

                    foreach (var span in spans)
                    {
                        Assert.Equal(expectedOperationName, span.Name);
                        Assert.Equal(expectedServiceName, span.Service);
                        Assert.Equal(SpanTypes.Http, span.Type);
                        Assert.Equal("HttpMessageHandler", span.Tags[Tags.InstrumentationName]);
                        Assert.False(span.Tags?.ContainsKey(Tags.Version), "External service span should not have service version tag.");

                        if (span.Tags[Tags.HttpStatusCode] == "502")
                        {
                            Assert.Equal(1, span.Error);
                        }
                    }

                    var firstSpan    = spans.First();
                    var traceId      = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.TraceId);
                    var parentSpanId = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.ParentId);

                    Assert.Equal(firstSpan.TraceId.ToString(CultureInfo.InvariantCulture), traceId);
                    Assert.Equal(firstSpan.SpanId.ToString(CultureInfo.InvariantCulture), parentSpanId);
                }
        }
        private void ConfigureInstrumentation(InliningOptions inlining, InstrumentationOptions instrumentation, bool enableSocketsHandler)
        {
            SetCallTargetSettings(inlining.EnableCallTarget, inlining.EnableInlining);

            // Should HttpClient try to use HttpSocketsHandler
            SetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER", enableSocketsHandler ? "1" : "0");

            // Enable specific integrations, or use defaults
            if (instrumentation.InstrumentSocketHandler.HasValue)
            {
                SetEnvironmentVariable("DD_HttpSocketsHandler_ENABLED", instrumentation.InstrumentSocketHandler.Value ? "true" : "false");
            }

            if (instrumentation.InstrumentWinHttpHandler.HasValue)
            {
                SetEnvironmentVariable("DD_WinHttpHandler_ENABLED", instrumentation.InstrumentWinHttpHandler.Value ? "true" : "false");
            }
        }
예제 #7
0
파일: ILInlining.cs 프로젝트: softuse/ILSpy
        internal static InliningOptions OptionsForBlock(Block block, int pos)
        {
            InliningOptions options = InliningOptions.None;

            if (IsCatchWhenBlock(block))
            {
                options |= InliningOptions.Aggressive;
            }
            else
            {
                var function = block.Ancestors.OfType <ILFunction>().FirstOrDefault();
                var inst     = block.Instructions[pos];
                if (IsInConstructorInitializer(function, inst))
                {
                    options |= InliningOptions.Aggressive;
                }
            }
            return(options);
        }
예제 #8
0
파일: ILInlining.cs 프로젝트: quitec/ilspy
        /// <summary>
        /// Inlines instructions before pos into block.Instructions[pos].
        /// </summary>
        /// <returns>The number of instructions that were inlined.</returns>
        public static int InlineInto(Block block, int pos, InliningOptions options, ILTransformContext context)
        {
            if (pos >= block.Instructions.Count)
            {
                return(0);
            }
            int count = 0;

            while (--pos >= 0)
            {
                if (InlineOneIfPossible(block, pos, options, context))
                {
                    count++;
                }
                else
                {
                    break;
                }
            }
            return(count);
        }
예제 #9
0
파일: ILInlining.cs 프로젝트: quitec/ilspy
        /// <summary>
        /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        public static bool InlineOne(StLoc stloc, InliningOptions options, ILTransformContext context)
        {
            ILVariable v     = stloc.Variable;
            Block      block = (Block)stloc.Parent;
            int        pos   = stloc.ChildIndex;

            if (DoInline(v, stloc.Value, block.Instructions.ElementAtOrDefault(pos + 1), options, context))
            {
                // Assign the ranges of the stloc instruction:
                stloc.Value.AddILRange(stloc);
                // Remove the stloc instruction:
                Debug.Assert(block.Instructions[pos] == stloc);
                block.Instructions.RemoveAt(pos);
                return(true);
            }
            else if (v.LoadCount == 0 && v.AddressCount == 0)
            {
                // The variable is never loaded
                if (SemanticHelper.IsPure(stloc.Value.Flags))
                {
                    // Remove completely if the instruction has no effects
                    // (except for reading locals)
                    context.Step("Remove dead store without side effects", stloc);
                    block.Instructions.RemoveAt(pos);
                    return(true);
                }
                else if (v.Kind == VariableKind.StackSlot)
                {
                    context.Step("Remove dead store, but keep expression", stloc);
                    // Assign the ranges of the stloc instruction:
                    stloc.Value.AddILRange(stloc);
                    // Remove the stloc, but keep the inner expression
                    stloc.ReplaceWith(stloc.Value);
                    return(true);
                }
            }
            return(false);
        }
예제 #10
0
파일: ILInlining.cs 프로젝트: quitec/ilspy
        public static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context)
        {
            bool modified     = false;
            var  instructions = block.Instructions;

            for (int i = instructions.Count - 1; i >= 0; i--)
            {
                if (instructions[i] is StLoc inst)
                {
                    InliningOptions options = InliningOptions.None;
                    if (context.Settings.AggressiveInlining || IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst))
                    {
                        options = InliningOptions.Aggressive;
                    }
                    if (InlineOneIfPossible(block, i, options, context))
                    {
                        modified = true;
                        continue;
                    }
                }
            }
            return(modified);
        }
예제 #11
0
파일: ILInlining.cs 프로젝트: quitec/ilspy
        internal static InliningOptions OptionsForBlock(Block block, int pos, ILTransformContext context)
        {
            InliningOptions options = InliningOptions.None;

            if (context.Settings.AggressiveInlining || IsCatchWhenBlock(block))
            {
                options |= InliningOptions.Aggressive;
            }
            else
            {
                var function = block.Ancestors.OfType <ILFunction>().FirstOrDefault();
                var inst     = block.Instructions[pos];
                if (IsInConstructorInitializer(function, inst))
                {
                    options |= InliningOptions.Aggressive;
                }
            }
            if (!context.Settings.UseRefLocalsForAccurateOrderOfEvaluation)
            {
                options |= InliningOptions.AllowChangingOrderOfEvaluationForExceptions;
            }
            return(options);
        }
예제 #12
0
파일: ILInlining.cs 프로젝트: quitec/ilspy
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, InliningOptions options)
        {
            if (expr == null)
            {
                return(FindResult.Stop);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                if (expr.SlotInfo == StObj.TargetSlot && !((StObj)expr.Parent).CanInlineIntoTargetSlot(expressionBeingMoved))
                {
                    if ((options & InliningOptions.AllowChangingOrderOfEvaluationForExceptions) != 0)
                    {
                        // Intentionally change code semantics so that we can avoid a ref local
                        if (expressionBeingMoved is LdFlda ldflda)
                        {
                            ldflda.DelayExceptions = true;
                        }
                        else if (expressionBeingMoved is LdElema ldelema)
                        {
                            ldelema.DelayExceptions = true;
                        }
                    }
                    else
                    {
                        // special case: the StObj.TargetSlot does not accept some kinds of expressions
                        return(FindResult.Stop);
                    }
                }
                return(FindResult.Found(expr));
            }
            else if (expr is Block block)
            {
                // Inlining into inline-blocks?
                switch (block.Kind)
                {
                case BlockKind.ControlFlow when block.Parent is BlockContainer:
                case BlockKind.ArrayInitializer:
                case BlockKind.CollectionInitializer:
                case BlockKind.ObjectInitializer:
                case BlockKind.CallInlineAssign:
                    // Allow inlining into the first instruction of the block
                    if (block.Instructions.Count == 0)
                    {
                        return(FindResult.Stop);
                    }
                    return(NoContinue(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, options)));

                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the block.
                case BlockKind.CallWithNamedArgs:
                    return(NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved));

                default:
                    return(FindResult.Stop);
                }
            }
            else if (options.HasFlag(InliningOptions.FindDeconstruction) && expr is DeconstructInstruction di)
            {
                return(FindResult.Deconstruction(di));
            }
            foreach (var child in expr.Children)
            {
                if (!expr.CanInlineIntoSlot(child.ChildIndex, expressionBeingMoved))
                {
                    return(FindResult.Stop);
                }

                // Recursively try to find the load instruction
                FindResult r = FindLoadInNext(child, v, expressionBeingMoved, options);
                if (r.Type != FindResultType.Continue)
                {
                    if (r.Type == FindResultType.Stop && (options & InliningOptions.IntroduceNamedArguments) != 0 && expr is CallInstruction call)
                    {
                        return(NamedArgumentTransform.CanIntroduceNamedArgument(call, child, v, expressionBeingMoved));
                    }
                    return(r);
                }
            }
            if (IsSafeForInlineOver(expr, expressionBeingMoved))
            {
                return(FindResult.Continue);                // continue searching
            }
            else
            {
                return(FindResult.Stop);                // abort, inlining not possible
            }
        }
예제 #13
0
파일: ILInlining.cs 프로젝트: quitec/ilspy
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context)
        {
            var r = FindLoadInNext(next, v, inlinedExpression, options);

            if (r.Type == FindResultType.Found || r.Type == FindResultType.NamedArgument)
            {
                var loadInst = r.LoadInst;
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary((LdLoca)loadInst, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    bool aggressive = (options & InliningOptions.Aggressive) != 0;
                    if (!aggressive && v.Kind != VariableKind.StackSlot &&
                        !NonAggressiveInlineInto(next, r, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                if (r.Type == FindResultType.NamedArgument)
                {
                    NamedArgumentTransform.IntroduceNamedArgument(r.CallArgument, context);
                    // Now that the argument is evaluated early, we can inline as usual
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    Debug.Assert(((LdLoca)loadInst).Variable == v);
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression, v.Type));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            return(false);
        }
예제 #14
0
        internal static bool DoInline(ILVariable v, StLoc originalStore, LdLoc loadInst, InliningOptions options, ILTransformContext context)
        {
            if ((options & InliningOptions.Aggressive) == 0 && originalStore.ILStackWasEmpty)
            {
                return(false);
            }
            context.Step($"Introduce named argument '{v.Name}'", originalStore);
            var call = (CallInstruction)loadInst.Parent;

            if (!(call.Parent is Block namedArgBlock) || namedArgBlock.Kind != BlockKind.CallWithNamedArgs)
            {
                // create namedArgBlock:
                namedArgBlock = new Block(BlockKind.CallWithNamedArgs);
                call.ReplaceWith(namedArgBlock);
                namedArgBlock.FinalInstruction = call;
                if (call.IsInstanceCall)
                {
                    IType thisVarType = call.Method.DeclaringType;
                    if (CallInstruction.ExpectedTypeForThisPointer(thisVarType) == StackType.Ref)
                    {
                        thisVarType = new ByReferenceType(thisVarType);
                    }
                    var function   = call.Ancestors.OfType <ILFunction>().First();
                    var thisArgVar = function.RegisterVariable(VariableKind.NamedArgument, thisVarType, "this_arg");
                    namedArgBlock.Instructions.Add(new StLoc(thisArgVar, call.Arguments[0]));
                    call.Arguments[0] = new LdLoc(thisArgVar);
                }
            }
            v.Kind = VariableKind.NamedArgument;
            namedArgBlock.Instructions.Insert(call.IsInstanceCall ? 1 : 0, originalStore);
            return(true);
        }
예제 #15
0
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context)
        {
            var r = FindLoadInNext(next, v, inlinedExpression, out var loadInst);

            if (r == FindResult.Found)
            {
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary(next, (LdLoca)loadInst, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    bool aggressive = (options & InliningOptions.Aggressive) != 0;
                    if (!aggressive && v.Kind != VariableKind.StackSlot &&
                        !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst.ILRange);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            else if (r == FindResult.NamedArgument && (options & InliningOptions.IntroduceNamedArguments) != 0)
            {
                return(NamedArgumentTransform.DoInline(v, (StLoc)inlinedExpression.Parent, (LdLoc)loadInst,
                                                       options, context));
            }
            return(false);
        }
예제 #16
0
파일: ILInlining.cs 프로젝트: softuse/ILSpy
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, InliningOptions options)
        {
            if (expr == null)
            {
                return(FindResult.Stop);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                return(FindResult.Found(expr));
            }
            else if (expr is Block block)
            {
                // Inlining into inline-blocks?
                switch (block.Kind)
                {
                case BlockKind.ArrayInitializer:
                case BlockKind.CollectionInitializer:
                case BlockKind.ObjectInitializer:
                case BlockKind.CallInlineAssign:
                    // Allow inlining into the first instruction of the block
                    if (block.Instructions.Count == 0)
                    {
                        return(FindResult.Stop);
                    }
                    return(NoContinue(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, options)));

                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the block.
                case BlockKind.CallWithNamedArgs:
                    return(NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved));

                default:
                    return(FindResult.Stop);
                }
            }
            else if (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1)
            {
                // Possibly a switch-container, allow inlining into the switch instruction:
                return(NoContinue(FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, options)));
                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the blockcontainer.
            }
예제 #17
0
파일: ILInlining.cs 프로젝트: jhoek/ILSpy
        /// <summary>
        /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
        /// </summary>
        /// <param name="loadInst">The load instruction (a descendant within 'next')</param>
        /// <param name="v">The variable being inlined.</param>
        static bool IsGeneratedValueTypeTemporary(LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression, InliningOptions options)
        {
            Debug.Assert(loadInst.Variable == v);
            // Inlining a value type variable is allowed only if the resulting code will maintain the semantics
            // that the method is operating on a copy.
            // Thus, we have to ensure we're operating on an r-value.
            // Additionally, we cannot inline in cases where the C# compiler prohibits the direct use
            // of the rvalue (e.g. M(ref (MyStruct)obj); is invalid).
            if (IsUsedAsThisPointerInCall(loadInst, out var method))
            {
                if (options.HasFlag(InliningOptions.Aggressive))
                {
                    // Inlining might be required in ctor initializers (see #2714).
                    // expressionBuilder.VisitAddressOf will handle creating the copy for us.
                    return(true);
                }

                switch (ClassifyExpression(inlinedExpression))
                {
                case ExpressionClassification.RValue:
                    // For struct method calls on rvalues, the C# compiler always generates temporaries.
                    return(true);

                case ExpressionClassification.MutableLValue:
                    // For struct method calls on mutable lvalues, the C# compiler never generates temporaries.
                    return(false);

                case ExpressionClassification.ReadonlyLValue:
                    // For struct method calls on readonly lvalues, the C# compiler
                    // only generates a temporary if it isn't a "readonly struct"
                    return(MethodRequiresCopyForReadonlyLValue(method));

                default:
                    throw new InvalidOperationException("invalid expression classification");
                }
            }
            else if (IsUsedAsThisPointerInFieldRead(loadInst))
            {
                // mcs generated temporaries for field reads on rvalues (#1555)
                return(ClassifyExpression(inlinedExpression) == ExpressionClassification.RValue);
            }
            else
            {
                return(false);
            }
        }