So, there are several things I want to talk about, but they all boil down to the perf of the Log View in the Build Details window in Visual Studio. Unfortunately, if you have a large amount of data here, it slows down everything until it gets fully loaded. We knew about some of the limitations here, but didn't realize how bad the problem was until after we shipped 2010. So, what are the issues and how do we mitigate the problem until it is completely fixed?

Errors and Warnings

The first major problem seems to be that lots of customers ignore warnings in their builds. These build up and eventually cause the slowness at around 10,000 errors or warnings. We have a fix ready for this very common problem in the SP1 patch. It should be released soon. If you have this problem, you need this service pack.

Activity Nodes

The next thing that seems to happen to a few customers (including our internal cusotmers inside Microsoft) is that you setup a loop in the Workflow. The loop ends up executing several thousand times and that leads to a large activity log. So, this is less common than the first issue but still ends up with the same result. The way to work around this is hide that activity from the log (unless you are in Diagnostic verbosity). You can do that by opening the XAML in a text editor and adding the following attribute to the activity elements that are showing up over and over again in the log. In this example, I am applying the attribute to a sequence, but it will work on any activity. If you really don't want to see the node at all, you can use a value of None.

    <Sequence mtbwt:BuildTrackingParticipant.Importance="Low">

 

This fix goes along with adjusting the verbosity of the build. You may want to try just lowering the verbosity on the build first. Verbosity is a process parameter on the Process tab of the build definition.

Project Nodes

The last issue seems very rare, but we have had customers report that their log opens very slowly due to the number of project nodes in the activity tree. This problem does not have a great workaround. The only fix I could come up with was to create a console application that would prune the project tree after the compilation was done. To get this problem you have to have about 500 projects with lots of dependencies between them, Those dependencies are the part that causes the additional length to the log view tree. So, here is the code I put in the console application and the XAML I used to call it.

Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Build.Common;
using Microsoft.TeamFoundation.Client;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("Usage: PruneBuildTree <collectionUrl> <buildUri>");
                return;
            }

            PruneProjectTree(new Uri(args[0]), new Uri(args[1]));
        }

        static void PruneProjectTree(Uri teamProjectCollectionUrl, Uri buildUri)
        {
            TfsTeamProjectCollection collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(teamProjectCollectionUrl);
            IBuildServer buildServer = collection.GetService<IBuildServer>();
            IBuildDetail build = buildServer.GetBuild(buildUri, new String[] { InformationTypes.BuildError, InformationTypes.BuildProject, InformationTypes.BuildWarning }, QueryOptions.None);

            // Get the top level projects and prune their dependency trees
            List<IBuildInformationNode> topLevelProjectNodes = build.Information.GetNodesByType(InformationTypes.BuildProject, false);
            foreach (IBuildInformationNode projectNode in topLevelProjectNodes)
            {
                PruneProjectNode(projectNode);
            }
        }

        // Depth first search for nodes that do not have any children
        // Note that project nodes, error nodes, and warning nodes are the only things
        // that should be here. So we will delete any branch that doesn't have errors or warnings.
        private static void PruneProjectNode(IBuildInformationNode projectNode)
        {
            foreach (IBuildInformationNode childNode in projectNode.Children.Nodes)
            {
                if (StringComparer.OrdinalIgnoreCase.Equals(childNode.Type, InformationTypes.BuildProject))
                {
                    PruneProjectNode(childNode);
                }
            }

            if (projectNode.Children.Nodes.Length == 0)
            {
                // remove this project node from its parent and from the server
                projectNode.Delete();
            }
        }
    }
}

XAML (added as the last thing in the Agent Scope Activity):

      <mtbwa:InvokeProcess Arguments="[&quot;http://jpricket-test3:8080/tfs/defaultcollection&quot; &amp; &quot; &quot; &amp; BuildDetail.Uri.AbsoluteUri]" FileName="[String.Format(&quot;{0}\\PruneBuildTree.exe&quot;, BinariesDirectory)]" WorkingDirectory="[BinariesDirectory]">
        <mtbwa:InvokeProcess.ErrorDataReceived>
          <ActivityAction x:TypeArguments="x:String">
            <ActivityAction.Argument>
              <DelegateInArgument x:TypeArguments="x:String" Name="errOutput" />
            </ActivityAction.Argument>
            <mtbwa:WriteBuildError />
          </ActivityAction>
        </mtbwa:InvokeProcess.ErrorDataReceived>
        <mtbwa:InvokeProcess.OutputDataReceived>
          <ActivityAction x:TypeArguments="x:String">
            <ActivityAction.Argument>
              <DelegateInArgument x:TypeArguments="x:String" Name="stdOutput" />
            </ActivityAction.Argument>
            <mtbwa:WriteBuildMessage Message="[stdOutput]" />
          </ActivityAction>
        </mtbwa:InvokeProcess.OutputDataReceived>
      </mtbwa:InvokeProcess>

Sorry for making this performance problem such a pain. We will get it fixed for all customers in the next release :)