Memory profiling within build process with MSBuild?

Use this forum for questions on how to use .NET Memory Profiler and how to analyse memory usage.
Post Reply
JuergenKlopf
Posts: 6
Joined: Wed Jul 11, 2012 1:48 pm

Memory profiling within build process with MSBuild?

Post by JuergenKlopf » Wed Jul 11, 2012 1:55 pm

Among other things we are using automated UI-Tests within our build process with MSBuild. Due to some problems we are trying now to integrate the .NET Memory Profiler within the build process and collect information (interesting values, errors, warnings, ...) during runnning the UI-Test.

Allows the api of the pofiler to do this?

Thank you very much in advance,
Jürgen

Andreas Suurkuusk
Posts: 1028
Joined: Wed Mar 02, 2005 7:53 pm

Re: Memory profiling within build process with MSBuild?

Post by Andreas Suurkuusk » Thu Jul 12, 2012 2:53 pm

You can do some automated testing using the profiler assertions and profiling the application using NetMemProfilerConsole. However, to extract additional information about the profiled process, the external profiler API needs to be used. You can get access to the external API by adding the SciTech.NetMemProfiler.Core assembly to you test project (you can find the assembly in the Assemblies directory in the installation folder). Unfortunately the documentation for the API is still far from finished, and it is not included with the installation.

The example below can give you an idea on how you can use the API to extract analysis issues. To run the sample, you have to download the latest build (v4.5.175). It is not yet officially released, but you can download it from here.

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using SciTech.Metadata;
using SciTech.Profiler;
using SciTech.Profiler.Memory;
using SciTech.Profiler.Memory.Analysis;

class Program
{
    private static void Main( string[] args )
    {
        ProfilerApplicationCore app = new ProfilerApplicationCore();

        // Define the process that should be profiled.
        ProfileProcessStartInfo startInfo = new ProfileProcessStartInfo(
            ProfilingType.Application,
            "<path to executable>" );

        // Create a new session (ActiveSession is derived from ProfilerSession)
        using( ActiveSession session = app.CreateSession( startInfo ) )
        {
            // Create an event that will signal that the profiled process has exited.
            using( ManualResetEvent stoppedEvent = new ManualResetEvent( false ) )
            {
                // Signal stopped event when session has stopped.
                session.StateChanged += ( o, e ) => 
                { 
                    if( session.State == ActiveSessionState.Stopped ) stoppedEvent.Set(); 
                };

                // Start the profiling.
                session.Start( TimeSpan.FromMinutes( 1 ) );

                // Wait for a while.
                Thread.Sleep( TimeSpan.FromSeconds( 10 ) );

                // And collect a snapshot.
                // NOTE! The "internal" API (MemProfiler.FullSnapShot) is probably more suitable
                // for collecting snapshots.
                session.CollectSnapshot( SnapshotCollection.FullGC );

                // Wait for the profiled process to exit (alternatively call session.Stop to 
                // terminate the process)
                stoppedEvent.WaitOne();
            }

            ProfilerComparison comparison = session.Comparison;

            // If a snapshot was collected, an automatic comparison has been performed (and 
            // a primary snapshot will be available).
            if( comparison.PrimarySnapshot != SnapshotHeader.Empty )
            {
                // Ignore the duplicate instances analyser, since it can take a very long 
                // time to perform. If duplicates instances information is wanted, do not Add 
                // the DuplicateInstance issue id to the ignored issues.
                IgnoredIssuesCollection ignoredIssues = new IgnoredIssuesCollection();
                ignoredIssues.Add( new IgnoredIssue( InstanceIssueIds.DuplicateInstance ) );

                // Analyse the comparison to get information about any issues.
                AnalysisResult analysisResult = comparison.AnalyseComparison( 
                    true, // Ignore runtime issues
                    new IgnoredIssuesCollection[] { ignoredIssues },
                    null // No pre-allocated AnalysisResult
                    );

                // Locate all instances that are marked with the 
                // "Direct event handler root" issue.
                var directEventHandlerInstances = analysisResult.GetInstancesWithIssues(
                    issueId => issueId == InstanceIssueIds.DirectEventHandler );

                foreach( var instanceId in directEventHandlerInstances )
                {
                    // Write call stack and root path information for all
                    // instances with a direct event handler root.
                    TypeInstance instance = comparison.GetTypeInstance( instanceId );

                    Console.WriteLine( "Instance: {0} ({1})", 
                        instanceId, 
                        instance.ManagedType );

                    var rootPaths = comparison.EnumRootPaths( instanceId, false, true );
                    // Find the first static field root path.
                    InstanceRootPath rootPath = rootPaths.FirstOrDefault( rp => 
                        rp.RootReferrer is StaticRootReferrer );

                    if( rootPath != null )
                    {
                        WriteRootPath( rootPath );
                    }

                    WriteCallStack( session.GetCallStackFunctions( instance.CallStack ) );
                }
            }
        }
    }

    private static void WriteRootPath( InstanceRootPath rootPath )
    {
        Console.WriteLine( "Root path: " );
        // A root path starts from the instance closest to the root (at index 0). 
        // Reverse the entries to present the root path in the same order as the 
        // .NET Memory Profiler UI.
        foreach( var entry in rootPath.Entries.Reverse() )
        {
            Console.WriteLine( "  " + entry );
        }
        Console.WriteLine( "  " + rootPath.RootReferrer );
        Console.WriteLine();
    }

    private static void WriteCallStack( CallStackFrame[] callStackFrames )
    {
        Console.WriteLine( "Call stack: " );
        foreach( var frame in callStackFrames )
        {
            Console.WriteLine( "  " + frame.ToString() );
        }
        Console.WriteLine();
    }
}
Best regards,

Andreas Suurkuusk
SciTech Software AB

Post Reply

Who is online

Users browsing this forum: No registered users and 27 guests