April, 2010

  • Kirill Osenkov

    Did I miss any C# syntax construct?

    • 14 Comments

    Dear readers, I’m asking for your valuable input again.

    I went through some of our test files and combined a single “all-in-one” C# syntax file that I want to contain all C# 4.0 constructs. I’m specifically not looking for combinations (because there’s an infinite number, obviously), it’s enough to just mention each syntax construct once.

    So, is there anything I’m missing? (Sorry, it’s a huge junkyard!)

    #error Error message
    #warning Warning message
    #pragma warning disable 414, 3021
    #pragma warning restore 3021
    #line 6
    #define foo
    #if foo
    #else
    #endif
    #undef foo
    //#warning foo
    //#error foo

    extern alias Foo;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using M = System.Math;

    #if DEBUG || TRACE
    using System.Diagnostics;
    #elif SILVERLIGHT
    using System.Diagnostics;
    #else
    using System.Diagnostics;
    #endif

    #region Region

    #region more
    using ConsoleApplication2.Test;
    #endregion
    using X = int1;
    using X = ABC.X<int>;

    #endregion

    [assembly: System.Copyright(@"(C) 2009")]
    [module: System.Copyright("\n\t\u0123(C) 2009" + "\u0123")]

    namespace My
    {
        using A.B;

        interface CoContra<out T, in K> { }
        delegate void CoContra2<out T, in K> () where T : struct;

        public unsafe partial class A : C, I
        {
            [method: Obsolete]
            public A([param: Obsolete] int foo) :
                base(1)
            {
            L:
                {
                    int i = sizeof(int);
                    ++i;
                }

    #if DEBUG
          Console.WriteLine(export.iefSupplied.command);
    #endif
                const int? local = int.MaxValue;
                const Guid? local0 = new Guid(r.ToString());

                var привет = local;
                var мир = local;
                var local3 = 0, local4 = 1;
                var local5 = null as Action ?? null;
                var local6 = local5 is Action;

                var u = 1u;
                var U = 1U;
                long hex = 0xBADC0DE, Hex = 0XDEADBEEF, l = -1L, L = 1L;
                ulong ul = 1ul, Ul = 1Ul, uL = 1uL, UL = 1UL,
                    lu = 1lu, Lu = 1Lu, lU = 1lU, LU = 1LU;

                bool @bool;
                byte @byte;
                char @char = 'c', \u0066 = '\u0066', hexchar = '\x0130', hexchar2 = (char)0xBAD;
                decimal @decimal = 1.44M;
                dynamic @dynamic;
                double @double = M.PI;
                float @float;
                int @int = local ?? -1;
                long @long;
                object @object;
                sbyte @sbyte;
                short @short;
                string @string = @"""/*";
                uint @uint;
                ulong @ulong;
                ushort @ushort;
               
                dynamic dynamic = local5;
                var add = 0;
                var ascending = 0;
                var descending = 0;
                var from = 0;
                var get = 0;
                var global = 0;
                var group = 0;
                var into = 0;
                var join = 0;
                var let = 0;
                var orderby = 0;
                var partial = 0;
                var remove = 0;
                var select = 0;
                var set = 0;
                var value = 0;
                var var = 0;
                var where = 0;
                var yield = 0;

                if (i > 0)
                {
                    return;
                }
                else if (i = 0)
                {
                    throw new Exception();
                }
                var o1 = new MyObject();
                var o2 = new MyObject(var);
                var o3 = new MyObject { A = i };
                var o4 = new MyObject(@dynamic)
                {
                    A = 0,
                    B = 0,
                    C = 0
                };
                var o5 = new { A = 0 };
                int[] a = new int[]
          {
            0,
            1,
            2,
            3,
            4,
            5
          };
                switch (i)
                {
                    case 1:
                        {
                            goto case 2;
                        }
                    case 2:
                        {
                            goto default;
                            break;
                        }
                    default:
                        {
                            return;
                        }
                }
                while (i < 10)
                {
                    ++i;
                }
                do
                {
                    ++i;
                }
                while (i < 10);
                for (int j = 0; j < 100; ++j)
                {
                    Console.WriteLine(j);
                }
                foreach (var i in Items())
                {
                    if (i == 7)
                        return;
                    else
                        continue;
                }
                checked
                {
                    checked(++i);
                }
                unchecked
                {
                    unchecked(++i);
                }
                lock (sync)
                    process();
                using (var v = BeginScope())
                using (A a = new A())
                using (BeginScope())
                    return;
                yield return this.items[3];
                yield break;
                fixed (int* p = stackalloc int[100])
                {
                    *intref = 1;
                }
                unsafe
                {
                    int* p = null;
                }
                try
                {
                    throw null;
                }
                catch (System.AccessViolationException av)
                {
                    throw av;
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                }
                var anonymous =
                {
                    A = 1,
                    B = 2,
                    C = 3,
                };
                var query = from c in customers
                            let d = c
                            where d != null
                            join c1 in customers on c1.GetHashCode() equals c.GetHashCode()
                            join c1 in customers on c1.GetHashCode() equals c.GetHashCode() into e
                            group c by c.Country
                                into g
                                orderby g.Count() ascending
                                orderby g.Key descending
                                select new { Country = g.Key, CustCount = g.Count() };
            }
            ~A()
            {
            }
            private readonly int f1;
            [Obsolete]
            [NonExisting]
            [Foo::NonExisting(var, 5)]
            [CLSCompliant(false)]
            [Obsolete, System.NonSerialized, NonSerialized, CLSCompliant(true || false & true)]
            private volatile int f2;
            [return: Obsolete]
            [method: Obsolete]
            public void Handler(object value)
            {
            }
            public int m<T>(T t)
              where T : class, new()
            {
                base.m(t);
                return 1;
            }
            public string P
            {
                get
                {
                    return "A";
                }
                set;
            }
            public abstract string P
            {
                get;
            }
            public abstract int this[int index]
            {
                protected internal get;
                internal protected set;
            }
            [method: Obsolete]
            [field: Obsolete]
            [event: Obsolete]
            public readonly event Event E;
            [event: Test]
            public event Action E1
            {
                add { value = value; }
                remove { }
            }
            public static A operator +(A first, A second)
            {
                Delegate handler = new Delegate(Handler);
                return first.Add(second);
            }
            [method: Obsolete]
            [return: Obsolete]
            public static bool operator true(A a)
            {
                return true;
            }
            public static bool operator false(A a)
            {
                return false;
            }
            class C
            {
            }
        }
        public struct S : I
        {
            public S()
            {
            }
            private int f1;
            [Obsolete]
            private volatile int f2;
            public abstract int m<T>(T t)
              where T : struct
            {
                return 1;
            }
            public string P
            {
                get
                {
                    int value = 0;
                    return "A";
                }
                set;
            }
            public abstract string P
            {
                get;
            }
            public abstract int this[int index]
            {
                get;
                internal protected set;
            }
            public event Event E;
            public static A operator +(A first, A second)
            {
                return first.Add(second);
            }
            fixed int field[10];
            class C
            {
            }
        }
        public interface I
        {
            void A(int value);
            string Value
            {
                get;
                set;
            }
        }
        [type: Flags]
        public enum E
        {
            A,
            B = A,
            C = 2 + A,

    #if DEBUG
        D,
    #endif

        }
        public delegate void Delegate(object P);
        namespace Test
        {
            using System;
            using System.Collections;
            public class Список
            {
                public static IEnumerable Power(int number, int exponent)
                {
                    Список Список = new Список();
                    Список.Main();
                    int counter = 0;
                    int אתר = 0;
                    while (++counter++ < --exponent--)
                    {
                        result = result * number + +number+++++number;
                        yield return result;
                    }
                }
                static void Main()
                {
                    foreach (int i in Power(2, 8))
                    {
                        Console.Write("{0} ", i);
                    }
                }
            }
        }
    }

    namespace ConsoleApplication1
    {
        namespace RecursiveGenericBaseType
        {
            class A<T> : B<A<T>, A<T>>
            {
                protected virtual A<T> M() { }
                protected abstract B<A<T>, A<T>> N() { }
                static B<A<T>, A<T>> O() { }
            }

            class B<T1, T2> : A<B<T1, T2>>
            {
                protected override A<T> M() { }
                protected sealed override B<A<T>, A<T>> N() { }
                new static A<T> O() { }
            }
        }

        namespace Boo
        {
            public class Bar<T> where T : IComparable
            {
                public T f;
                public class Foo<U> : IEnumerable<T>
                {
                    public void Method<K, V>(K k, T t, U u)
                        where K : IList<V>, IList<T>, IList<U>
                        where V : IList<K>
                    {
                        A<int> a;
                    }
                }
            }
        }

        class Test
        {
            void Bar3()
            {
                var x = new Boo.Bar<int>.Foo<object>();
                x.Method<string, string>(" ", 5, new object());

                var q = from i in new int[] { 1, 2, 3, 4 }
                        where i > 5
                        select i;
            }

            public static implicit operator Test(string s)
            {
                return new ConsoleApplication1.Test();
            }
            public static explicit operator Test(string s)
            {
                return new Test();
            }

            public int foo = 5;
            void Bar2()
            {
                foo = 6;
                this.Foo = 5.GetType(); Test t = "sss";
            }

            void Blah()
            {
                int i = 5;
                int? j = 6;

                Expression<Func<int>> e = () => i;
            }

            public Type Foo
            {
                get
                {
                    return typeof(System.Int32);
                }
                set
                {
                    var t = typeof(System.Int32);
                    t.ToString();
                    t = value;
                }
            }

            public void Constants()
            {
                int i = 1 + 2 + 3 + 5;
                global::System.String s = "a" + (System.String)"a" + "a" + "a" + "a" + "A";
            }

            public void ConstructedType()
            {
                List<int> i = null;
                int c = i.Count;
            }
        }
    }

    namespace Comments.XmlComments.UndocumentedKeywords
    {
        /// <summary>
        /// Whatever
        /// </summary>
        /// <!-- c -->
        /// <![CDATA[c]]> //
        /// <c></c> /* */
        /// <code></code>
        /// <example></example>
        /// <exception cref="bla"></exception>
        /// <include file='' path='[@name=""]'/>
        /// <permission cref=" "></permission>
        /// <remarks></remarks>
        /// <see cref=""/>
        /// <seealso cref=" "/>
        /// <value></value>
        /// <typeparam name="T"></typeparam>
        class /*///*/C<T>
        {
            void M<U>(T t, U u)
            {
                // comment
                /* *** / */
                /* //
                 */
                /*s*///comment
                /*s*/int /*s*/intValue = 0;
                intValue = intValue /*s*/+ 1;
                string strValue = /*s*/"hello";
                /*s*/MyClass c = new MyClass();
                string verbatimStr = /*s*/@"\\\\";
            }
        }

        //General Test F. Type a very long class name, verify colorization happens correctly only upto the correct size (118324)
        class TestClassXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/*Scen8*/{ }

        class TestClassXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX22/*Scen9*/{ }

        class yield
        {
            void Foo<U>(__arglist)
            {
                C<U> c = null;
                c.M<int>(5, default(U));
                TypedReference tr = __makeref(c);
                Type t = __reftype(tr);
                int j = __refvalue(tr, int);
                Params(a: t, b: t);
            }
            void Params(ref dynamic a, out dynamic b, params dynamic[] c) {}
            void Params(out dynamic a = 2, ref dynamic c = default(dynamic), params dynamic[][] c) {}

            public override string ToString() { return base.ToString(); }

            public partial void method()
            {
                int?[] a = new int?[5];/*[] bug*/ // YES []
                int[] var = { 1, 2, 3, 4, 5 };/*,;*/
                int i = a[i];/*[]*/
                Foo<T> f = new Foo<int>();/*<> ()*/
                f.method();/*().*/
                i = i + i - i * i / i % i & i | i ^ i;/*+ - * / % & | ^*/
                bool b = true & false | true ^ false;/*& | ^*/
                b = !b;/*!*/
                i = ~i;/*~i*/
                b = i < i && i > i;/*< && >*/
                int? ii = 5;/*? bug*/ // NO ?
                int f = true ? 1 : 0;/*? :*/   // YES :
                i++;/*++*/
                i--;/*--*/
                b = true && false || true;/*&& ||*/
                i << 5;/*<<*/
                i >> 5;/*>>*/
                b = i == i && i != i && i <= i && i >= i;/*= == && != <= >=*/
                i += 5.0;/*+=*/
                i -= i;/*-=*/
                i *= i;/**=*/
                i /= i;/*/=*/
                i %= i;/*%=*/
                i &= i;/*&=*/
                i |= i;/*|=*/
                i ^= i;/*^=*/
                i <<= i;/*<<=*/
                i >>= i;/*>>=*/
                object s = x => x + 1;/*=>*/
                Point point;
                unsafe
                {
                    Point* p = &point;/** &*/
                    p->x = 10;/*->*/
                }
                IO::BinaryReader br = null;
            }

            struct Point { public int X; public int Y; }
        }
  • Kirill Osenkov

    Creating images with XAML

    • 2 Comments

    In comments to my previous post Benjamin had asked how did I create an image like this:

    I used standard PowerPoint SmartArt graphics – it offers rich possibilities and is easy enough to use.

    But it suddenly occurred to me that it should be relatively straightforward to use XAML to create a graphic like this – all in all, this is what XAML is actually for. So here's a quick version of that graphic created in Visual Studio 2010 using XAML:

    image

    And here's the XAML for it (if you're using WPF version prior to 4.0 remove UseLayoutRounding and TextOptions.TextFormattingMode attributes):

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="287" Width="519"
            UseLayoutRounding="True"
            TextOptions.TextFormattingMode="Display"
            WindowStartupLocation="CenterScreen">
        <Window.Resources>
            <Style TargetType="TextBlock">
                <Setter Property="FontSize" Value="12" />
                <Setter Property="FontFamily" Value="Verdana" />
            </Style>
            <Style x:Key="Shadow" TargetType="FrameworkElement">
                <Setter Property="BitmapEffect">
                    <Setter.Value>
                        <DropShadowBitmapEffect
                            Color="DarkGray"
                            Direction="315"
                            Opacity="0.5"
                            Softness="0.2"
                            ShadowDepth="3"/>
                    </Setter.Value>
                </Setter>
            </Style>
            <Style x:Key="Byte" TargetType="Border" BasedOn="{StaticResource Shadow}">
                <Setter Property="Background" Value="White"/>
                <Setter Property="BorderThickness" Value="1" />
                <Setter Property="BorderBrush" Value="DimGray" />
                <Setter Property="Padding" Value="12,6" />
                <Setter Property="Margin" Value="4, 2, 0, 2" />
                <Setter Property="MinWidth" Value="35" />
            </Style>
            <LinearGradientBrush x:Key="Gradient1" StartPoint="0,0" EndPoint="0,1">
                <GradientStopCollection>
                    <GradientStop Color="Honeydew" Offset="0" />
                    <GradientStop Color="#D0FFC0" Offset="1" />
                </GradientStopCollection>
            </LinearGradientBrush>
            <Style x:Key="SelectedByte" BasedOn="{StaticResource Byte}" TargetType="Border">
                <Setter Property="Background" Value="{StaticResource Gradient1}" />
            </Style>
            <Style x:Key="Arrow" TargetType="Polygon" BasedOn="{StaticResource Shadow}">
                <Setter Property="Fill" Value="{StaticResource Gradient1}" />
            </Style>
        </Window.Resources>
        <Grid>
            <Canvas>
                <Polygon 
                    Style="{StaticResource Arrow}"
                    Canvas.Left="251"
                    Canvas.Top="36.5"
                    Fill="{StaticResource Gradient1}" 
                    StrokeThickness="1"
                    Stroke="DarkGreen" 
                    Points="10,0 10,25 5,25 15,40 25,25 20,25 20,0 20,0" />
                <Polygon
                    Canvas.Left="102"
                    Canvas.Top="111"
                    Fill="{StaticResource Gradient1}" 
                    Points="0,0 162,0 201,40 0,40" />
                <TextBlock 
                    Canvas.Left="282" 
                    Canvas.Top="36">
                    Replace continues from here,<LineBreak /> no further matches
                </TextBlock>
                <TextBlock 
                    Canvas.Left="199" 
                    Canvas.Top="197">
                    Need a second pass to replace this one
                </TextBlock>
                <StackPanel 
                    Canvas.Left="20" 
                    Canvas.Top="80" 
                    Orientation="Horizontal">
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>a</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>b</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>c</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>d</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock> </TextBlock>
                    </Border>
                </StackPanel>
                <StackPanel 
                    Canvas.Left="20" 
                    Canvas.Top="150" 
                    Orientation="Horizontal" 
                    VerticalAlignment="Top">
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>a</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>b</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock> </TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>c</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>d</TextBlock>
                    </Border>
                </StackPanel>
                <Polyline
                    Canvas.Left="227"
                    Canvas.Top="186"
                    Points="0,0 0,5 159,5 159,0"
                    Stroke="Green"
                    StrokeThickness="2">
                </Polyline>
            </Canvas>
        </Grid>
    </Window>
  • Kirill Osenkov

    Stress testing Visual Studio 2010 – Part 3

    • 3 Comments

    Finally I have some time to continue my series on how we did stress testing on the languages team to uncover crashes, hangs and leaks in Visual Studio 2010.

    As a reminder, Part 1 gave a general overview of what we do for stress (8-hour runs) and Part 2 listed the parameters that we’re measuring (VM, working set, private memory, committed memory, managed memory, GC handles, CCW, GDI handles, USER objects, OS handles and the number of threads).

    In this part I will tell more about what types of tests we run and how we look for bugs. As a reminder, I was responsible for stress-testing four parts of VS: C# IDE, VB IDE, F# IDE and the Hostable Editor (Workflow designer can host our compilers to provide IntelliSense and colorization for their expression editors).

    On each team, we had one master test script that was invoking each feature sequentially in an end-to-end fashion. For example, for C# I had a test called ‘AllCSharpFeatures’ that invokes 28 features (we say it has 28 ‘scenarios’). Here they are:

    1. Formatting
    2. Colorization
    3. Completion List
    4. Quick Info
    5. Parameter Help
    6. Go To Definition
    7. Code Definition Window
    8. Find All References
    9. Navigate To
    10. Call Hierarchy Engine
    11. Call Hierarchy Toolwindow
    12. Highlight References
    13. Navigation Bar
    14. Metadata As Source
    15. Live Squiggles
    16. Generate From Usage
    17. Generate Type Dialog
    18. Add Using SmartTag
    19. Implement Interface
    20. += event handler generation
    21. Rename
    22. Rename SmartTag
    23. Extract Method
    24. Extract Interface
    25. Encapsulate Field
    26. Reorder / Remove Parameters
    27. Edit-and-Continue
    28. Build

    The test is written in such a way that if all the 28 scenarios are run sequentially, it models a typical user working with features (the scenarios are augmented with typing, inserting and removing text to keep the flow). Moreover, if you disable any of the scenarios, the remaining workflow can still execute (they’re independent of each other). Finally, each scenario is repeatable, so that you can call it twice or more times (“idempotent”).

    While the test is running, we record the parameters that I mentioned in Part 2 to an XML-based log file, before and after every scenario.

    Now with all that set up, we run the test in one of several modes. First mode is called end-to-end stress iterations – we just repeat the entire sequence 30 or 100 times or 8 hours and after it’s done, the XML log is converted to an Excel spreadsheet with charts like this:

    image

    This is an 8-hour run from November 19 2009. The main test has looped 52 times (a total of 1474 executed scenarios). We clearly see that we’re leaking about 70 MB of working set and about the same amount of VM. However from this chart it’s hard to tell which feature is leaking.

    Next step is that we run the test in ‘scenario iterations’ mode. First, we run the entire sequence once to “warm up” (load all modules, JIT all the code, fill the caches) and then we loop the first scenario (Formatting) 30 times, then we loop the second scenario (Colorization) 30 times, and so on. The fact that the scenarios are composable and repeatable allows us to do that. Now we get a different picture (notice the warm-up growth at the very beginning):

    image

    A handle is typically a native resource, such as a file, registry key, thread or synchronization object. This chart clearly shows that during our “Navigate To” scenario (that emulates the user pressing Ctrl+, searching for a member and navigating there) we lose around 600 OS handles, which, divided by 30 iterations, gives us a leak of 20 handles per iteration. Now we have a bug with repro steps (using Navigate To leaks 20 handles per iteration) and our developer can use htrace to find the leak.

    image

    The GC Handles page shows a leak of GC handles in += event handler generation code, as well as in our Edit-and-Continue scenario. We can then use !gchandles in sos.dll to find and isolate the bug. The huge spike in EnC means that when the debugger starts, we allocate a large amount of resources, and when it stops, we free almost all of them back. The difference (that the debugger didn’t release) is an actual leak. You can also see that the debugger leaks these continuously and not in one swoop because you can see the ragged upward slope when the debugger is running.

    image

    Finally, this sample shows a leak of Com-callable wrappers (CCW) in the Generate From Usage scenario.

    To reiterate, we can run our test in two modes: sequential loop to stress end-to-end feature usage, and stress each feature separately (with one warm-up iteration to separate the resource initialization from real leaks). David Nelson asked in the comments to Part 1, if we’re running our tests in combination with each other, as well as in isolation. This gives the answer that, yes, we do. We can run each of the scenarios for 8 hours separately to stress it on its own and determine an exact amount of leak without external noise, as well as run a more real-world stress in a loop, where scenarios work together and are invoked one after each other in combination.

    This way we can also measure the duration of each scenario and compare it to earlier builds to see if we have improved performance or if we have accidental slowdown in any of the scenarios. An interesting phenomena that I observed at the early stages is that all of our scenarios were mysteriously slowing down after hours of work. After a lot of head scratching I tracked it down to the function that was writing stress data to the XML log. After 5-6 hours of constant logging our XML stress log got into the sizes of 15-20 MB and we were using DOM to read the entire XML file, add an entry and write it all back. No wonder our scenarios slowed down! I replaced this code with a simple File.AppendAllText that works in O(1) and we were all good again (reminder: next time don’t forget to append </StressLog> at the end of the test!).

    To conclude, I’d like to list some pointers to different tools we use to measure resources and track down leaks.

  • Kirill Osenkov

    I’m speaking at Seattle CodeCamp 2010 on Saturday, April 17

    • 2 Comments

    Seattle CodeCamp 2010 will be held the weekend of April 17-18 at the Microsoft Campus in Redmond WA (“The Commons”). On Saturday, April 17 at 4pm I’ll be giving a talk titled “Inside LiveGeometry – architecture of a Silverlight application”.

    Abstract:

    Live Geometry (http://livegeometry.codeplex.com) is an open-source Silverlight application that lets you create interactive ruler-and-compass constructions and experiment with them. It is a CAD-like educational software for teachers and students that helps visualize and solve geometry problems.

    I will talk about the internals and implementation details:

    as well as dive into various details driven by the audience interests. You can also ask me questions related to Visual Studio, C# and VB languages, C# and VB IDE features, WPF and Silverlight, testing and other topics.

    The schedule is here: https://seattle.codecamp.us/schedule.aspx. Take a look, there will be many interesting talks if you’re in the area. Kevin Pilch-Bisson, our C# IDE Dev Lead, will be talking about C# IDE new features in VS 2010. It’s a great chance to talk to him about Visual Studio, our existing features and planning for the next VS release after 2010. Kevin is very knowledgeable and engaged, so don’t miss this one.

    The full list of sessions is here: https://seattle.codecamp.us/sessions.aspx.

  • Kirill Osenkov

    Recording of my “Inside Live Geometry” talk now available

    • 0 Comments

    Boost Virtual Events and Tri-Digital have recorded the talk that I gave during Seattle CodeCamp 2010.

    Check out their interactive site built fully with Silverlight:

    http://events.boostweb20.com/Events/SeattleCodeCamp2010/

    The deep link to my talk recording is here:

    http://events.boostweb20.com/Events/SeattleCodeCamp2010/default.aspx#state=sessionCode%242011-6

    The talk is about an hour long, I briefly demo the application (http://livegeometry.com) and then walk the audience through the source code, explaining the purpose of the different parts of the application and how they work together.

Page 1 of 1 (5 items)