.NET Profiler API - Attaching to process

Use this forum for questions on how to use .NET Memory Profiler and how to analyse memory usage.
Post Reply
Niko
Posts: 2
Joined: Thu Apr 11, 2019 8:16 am

.NET Profiler API - Attaching to process

Post by Niko » Thu Apr 11, 2019 8:47 am

Hello,

I am working on a external memory profiling test, i.e. I have to attach to another process and analyze its memory profile.

I have a professional license for the NetMemProfiler5, and I am working with the 'SciTech.NetMemProfiler.Core.dll' version 5.6.46.0.

My issue is that I am unable to attach to a process, take a snapshot, run some C# code of indefinite length and then take another snapshot without using the GUI.

I have tried the following things:

1. Using the classes defined in: "SciTech.Profiler.ProfilerApplicationCore"
- My issue with this approach is that I am unable to create a "SciTech.Profiler.ActiveSession" for a process which is already running.

My code for profiling a new process looks like this:

Code: Select all

string applicationPath = "PathToApplication";
ProfilerApplicationCore application = new ProfilerApplicationCore();
ProfileProcessStartInfo profileProcessStartInfo = new ProfileProcessStartInfo(ProfilingType.Application, applicationPath);
profileProcessStartInfo.ProgramArguments = "-embedding";
ActiveSession activeSession = application.CreateSession(profileProcessStartInfo);
- Question: Is it possible to adapt this code such that I receive an ActiveSession for an already running process, similar to an "Attach to Process"?

2. Using NmpCore.exe
- My command looks similar to this: .\NmpCore.exe /a:20476 /cs1 /sf:C:\SessionFile\Session1
- By running this command, then running some C# code and then running this command again, I receive two session-files which I can load via the following code:

Code: Select all

ProfilerApplicationCore application = new ProfilerApplicationCore();
ProfilerSession session_1 = application.LoadSession(@"C:\SessionFile\SessionFile1.prfsession");
- However, In this approach I am unable to compare two snapshots from different sessions. The method 'ProfilerApplicationCore.CompareSnapshots()' is at least applicable only to a single session.

- Question: Is it possible to compare snapshots from different sessions using version 5.6.46.0?


Is there another way to programmatically achieve my goal? Thank you in advance.

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

Re: .NET Profiler API - Attaching to process

Post by Andreas Suurkuusk » Mon Apr 15, 2019 7:35 am

1. To attach to a running process using the API you need to specify the profiling type "Attach" and the id of the process to attach to. The code below shows how you can attach to a process using the API.

Code: Select all

static void Main(string[] args)
{
    int processId = 21284;  // Id of process

    ProfilerApplicationCore application = new ProfilerApplicationCore();
    // Using ProfilingType.Attach to attach to a process.
    // The name argument is just used for presentation. The process id must be specified explicitly.
    ProfileProcessStartInfo profileProcessStartInfo = new ProfileProcessStartInfo(ProfilingType.Attach, "SomeProcess");
    // Specify the id of the process to profile
    profileProcessStartInfo.ProcessId = processId;

    // Indicate whether the .NET Profiler API should be used (AttachType.UsingAPI) or whether inspecting profiling
    // should be enabled (AttachType.NoAPI) 
    profileProcessStartInfo.AttachType = AttachType.NoAPI;
    profileProcessStartInfo.UseDumpFile = true;

    // Create session
    using (ActiveSession activeSession = application.CreateSession(profileProcessStartInfo))
    {
        // Start it 
        activeSession.Start(TimeSpan.FromMinutes(1));
        // Collect a snapshot
        activeSession.CollectSnapshot(SnapshotCollection.FullGC);

        // And stop it
        activeSession.Stop();

        // Save session; with all snapshots and real-time data
        activeSession.Save(@"<path to session>", null, true);
    }
}
2. To compare snapshot from different sessions, you need to load the session manually and create a comparison instance with the help of the application instance. The ProfilerComparison has a CompareSnapshots overload that accepts two process snapshot header. These snapshots may com from different files. The code below shows how you can compare two snapshots from two different sessions.

Code: Select all

static void Main(string[] args)
{
    ProfilerApplicationCore application = new ProfilerApplicationCore();

    // Load sessions
    using (ProfilerSession session1 = application.LoadSession(@"<path to session 1>"))
    using (ProfilerSession session2 = application.LoadSession(@"<path to session 2>"))
    {
        var comparison = application.CreateComparison();
        comparison.CompareSnapshots(session1.SessionFile.GetSnapshotHeaders()[0].Processes[0],
            session2.SessionFile.GetSnapshotHeaders()[0].Processes[0]
            );

        Console.WriteLine(comparison.HasNewAndRemovedData ? "Comparison has data about new and removed instances" : "Comparison does not have data about new and removed instances");

        foreach (var comparedType in comparison.GetComparedTypes())
        {
            if (comparedType.DeltaLiveBytesCount != 0)
            {
                Console.WriteLine($"{comparedType.FullName} - Delta bytes: {comparedType.DeltaLiveBytesCount}");
            }
        }
    }
}
Note that the comparison information is reduced when two different session are involved. This is something that will be significantly improved in the next major release of the profiler.


I hope this helps. If you need any further assistance, please reply to this post.
Best regards,

Andreas Suurkuusk
SciTech Software AB

Niko
Posts: 2
Joined: Thu Apr 11, 2019 8:16 am

Re: .NET Profiler API - Attaching to process

Post by Niko » Wed Apr 17, 2019 2:15 pm

Thank you for the perfect reply. Both approaches now work for me.

I have another question regarding the analysis of issues. If I open a session-file in the .NET Memory Profiler GUI, in the 'Overview' tab, I see a list of issues. Of special interest to me are issues in which the column 'Analysis issue' is marked with a black exclamation mark inside a yellow triangle, which I would interpret as issues of higher relevance than, say, a possible duplicate instance issue.

Is it possible to access this list of issues programmatically as well? My current code looks similar to this, but is unable to retrieve any issues:

Code: Select all

using (ActiveSession activeSession = application.CreateSession(profileProcessStartInfo))
{
                activeSession.Start(TimeSpan.FromMinutes(1));
                activeSession.CollectSnapshot(SnapshotCollection.FullGC);

                //Some code is executed here

                activeSession.CollectSnapshot(SnapshotCollection.FullGC);

                AnalysisResult analysis = new AnalysisResult(activeSession.Comparison);

                foreach (var typeInfo in activeSession.Comparison.GetComparedTypes())
                {
                    foreach (FoundAnalysisIssue issue in analysis.GetTypeIssues(typeInfo.Types[0], OpInfoToken.Empty))
                    {

                    }
                }
}
More specifically, "analysis.GetTypeIssues()" seems to return no issues, although there exist issues if I open the corresponding session file via the GUI.

- Question 1: Is it possible to access the issues visible in the .Net Memory Profiler GUI via the API as well, if so: How do I need to adapt my code?

- Question 2: How do I distinguish between more severe and less severe issues via the .Net Memory Profiler API?

Kind regards,
Niko

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

Re: .NET Profiler API - Attaching to process

Post by Andreas Suurkuusk » Tue Apr 23, 2019 5:58 am

To retrieve analysis issues from a comparison you need to run the comparison analyzer. The analyzer is run by calling AnalyseComparison on the comparison. THis method returns an AnalysisResult which can be used when investigating issues. The code below shows how warning and error issues can be printed for a comparison:

Code: Select all

ProfilerComparison comparison = ...;

// 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.
var ignoredIssues = new IgnoredIssuesCollection();
ignoredIssues.Add(new IgnoredIssue(InstanceIssueIds.DuplicateInstance));

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

foreach(var typeWithIssue in analysisResult.GetTypesWithIssues())
{
    var typeIssues = analysisResult.GetTypeIssues(typeWithIssue, OpInfoToken.Empty);

    // Select warning and error issues.
    var warningAndErrorIssues = typeIssues.Where(ti => ti.Level >= AnalysisInfoLevel.Warning);
    foreach( FoundAnalysisIssue foundIssue in warningAndErrorIssues)
    {
        // FoundAnalysisIssue contains information about the Issue, related links, and 
        // the source of the issue.                        
        AnalysisIssue  issue = foundIssue.Issue;
        Console.WriteLine($"Issue in type '{typeWithIssue}': {issue.Title}");
    }                    
}
This code can for instance be inserted into the attach to process code in my previous reply.

Code: Select all

static void Main(string[] args)
{
    int processId = 13528;  // Id of process

    ProfilerApplicationCore application = new ProfilerApplicationCore();
    // Using ProfilingType.Attach to attach to a process.
    // The name argument is just used for presentation. The process id must be specified explicitly.
    ProfileProcessStartInfo profileProcessStartInfo = new ProfileProcessStartInfo(ProfilingType.Attach, "SomeProcess");
    // Specify the id of the process to profile
    profileProcessStartInfo.ProcessId = processId;

    // Indicate whether the .NET Profiler API should be used (AttachType.UsingAPI) or whether inspecting profiling
    // should be enabled (AttachType.NoAPI) 
    profileProcessStartInfo.AttachType = AttachType.NoAPI;
    profileProcessStartInfo.UseDumpFile = true;

    // Create session
    using (ActiveSession activeSession = application.CreateSession(profileProcessStartInfo))
    {
        // Start it 
        activeSession.Start(TimeSpan.FromMinutes(1));
        // Collect a snapshot
        activeSession.CollectSnapshot(SnapshotCollection.FullGC);

        // And stop it
        activeSession.Stop();

        ProfilerComparison comparison = activeSession.Comparison;

        // 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.
        var ignoredIssues = new IgnoredIssuesCollection();
        ignoredIssues.Add(new IgnoredIssue(InstanceIssueIds.DuplicateInstance));

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

        foreach(var typeWithIssue in analysisResult.GetTypesWithIssues())
        {
            var typeIssues = analysisResult.GetTypeIssues(typeWithIssue, OpInfoToken.Empty);

            // Select warning and error issues.
            var warningAndErrorIssues = typeIssues.Where(ti => ti.Level >= AnalysisInfoLevel.Warning);
            foreach( FoundAnalysisIssue foundIssue in warningAndErrorIssues)
            {
                // FoundAnalysisIssue contains information about the Issue, related links, and 
                // the source of the issue.                        
                AnalysisIssue  issue = foundIssue.Issue;
                Console.WriteLine($"Issue in type '{typeWithIssue}': {issue.Title}");
            }                    
        }
    }
}
  1. The code above shows how to access analysis issues.
  2. You can use the Level property of FoundAnalysisIssue/AnalysisIssue to check the severity of the issue.

I'm sorry for the delayed reply to your post, I have been away on Easter holiddays.
Best regards,

Andreas Suurkuusk
SciTech Software AB

Post Reply

Who is online

Users browsing this forum: No registered users and 12 guests