Memory Leak – Microsoft.Reporting.WinForms.LocalReport

Several weeks ago I was asked to assist with a sluggish ASP.NET C# MVC application. This application is an OLTP system and processes several thousand orders a day. It was hosted on a Windows 2012 R2 64bit 16 Core machine. After discussing with the developer, I was told that the application has acceptable levels of performance for a while and then becomes sluggish (this behavior suggests some kind of  a leak). At times, it becomes extremely slow and rebooting the machine solves the problem. The problem exhibits itself more frequently during days when there were above average volumes.

Downloading Process Explorer from SysInternals, I found the w3wp.exe (there were several, but 2 of them stood out). Process Explorer does not display handle information by default. I will probably blog about this separately. There are many articles out there that explain how to look for Handles using Process Explorer. This should get you started. Here is another excellent blog explaining the theoretical limit of handles. It’s a theoretical limit because there are many types of thresholds that an application can reach which causes severe performance degradation.

I knew that we had a potential handle leak. There were also other issues within the application, that were not related to the handle leak. The challenge was to isolate the handle leak versus defects in the program logic, 3rd party hardware behaviors (like non genuine printer cartridges) and various other user errors.  Debugging a live system with several 100 users connected wasn’t happening. We were able to get a 4.5Gb memory dump.

When I moved the memory dump to a machine where I could analyze it, I found that the Production server was running an older version of the .NET Framework. The MS Symbol server wasn’t much help. I ended up copying the complete framework64 folder to the local machine (a different folder, not the Microsoft.NET\Framework folder) and loaded SOS using the .load command.

01-load sos
Using the time command, the overall CPU utilization by the Process compared to the System uptime showed that it wasn’t CPU contention.

02-time

The dumpheap command displays the managed heap. The output contains the Method Table, Object Instance Count, Memory Allocated and the Type from left to right. !dumpheap -stat reported about 164k lines of output and 9.7M object references. Many of these objects pointed to dynamically created Report Expressions.

01-dumpheap -stat

Typically the next logical step is to use !dumpheap -mt <<method table address>> to display all the individual object instances and then reviewing the individual objects to understand them better.

02-dumpheap -stat

Looking for loaded modules using the !eeheap -loader command showed that the worker process had 2622 App Domains (with a heapsize of 537mb). Dumping one of the modules using !DumpModule command pointed to a Dynamically loaded Expression Library associated with SQL Server Reporting Services.

05-eeheap -loader06-eeheap -loader

At this point, I knew that the issue may be related to the Dynamically Loaded Expression Assembly and its associated AppDomain that failed to unload. What was keeping the AppDomain from unloading? The dynamic expression library is used by the LocalReport object, which is part of the Microsoft.Reporting.Winforms namespace.

Further research showed that creating a new AppDomain is per design. The developers @ Microsoft had identified an issue with the dynamic expression library being loaded into the Default AppDomain and using up more memory each time the LocalReport object was created. An assembly once loaded into an AppDomain can never be unloaded. Hence the dynamic assembly was loaded in a new AppDomain and if all goes well, the AppDomain can be safely unloaded. As per the documentation calling LocalReport.ReleaseSandBoxAppDomain should unload the AppDomain. Unfortunately, the unload never happens because there is a problem with the internal implementation of the Dispose Method.

Using .NET Memory Profiler (JetBrains dotPeek), I was able to identify the Event Delegate

Screen Shot 2017-04-11 at 11.13.42 AM

Looking at the implementation, the handlers are not removed in the Dispose method. This in turn keeps the AppDomain from unloading. Hope this gets resolved in a future release. For now, we are recycling the Application Pool more frequently to work around the memory leak and the developers are looking at an alternate solution to print the labels.

08

One of the questions that I was asked while presenting my findings with the project manager, is why did this suddenly become a problem, since the application had been in Production for over 2 years. The issue had been there from the very beginning. The OOTB Application Pool Recycle was  causing the host process to shutdown after a period of inactivity (this app was used only between 8am-5pm). So it had enough time to shutdown after hours and the problem would never surface until the transaction volume increased, at which point the application started hitting thresholds.

It always helps to set baselines. Keep tabs on System configuration, Transaction Volume and major system change events. This will greatly shorten the time it takes to troubleshoot performance issues.

 

 

WinDbg – Welcome to Logging

Performing dump analysis is often times challenging due to the nature of the beast, the UI or the lack thereof :). Some commands are extremely time consuming or like me, just hate scrolling. Also there is only so much that you can view by scrolling.

Logging can be enabled before running commands that produce tons of output. We can also create seperate log files for each command by setting up separate log files before executing each command. From here, we can open the log files in an External Editor like Notepad++ and work with WinDbg on another monitor.

To enable Logging, goto the Edit Menu -> Open/Close Log File…

01

provide the Log file name and click ok. You are all done. Check the Append checkbox to append to an existing file or else the file gets overwritten.

02

After I am done capturing the logs that I need, I come back and Close Open Log File button to close the open file. This disables logging, while I take the file offline for Analysis.

The output can also be copied to excel and formatted to quickly sift though.

.foreach WinDbg

Recursively executing a command in WinDbg is one of the coolest features. In the example below, I used one method table from the output of a !FinalizeQueue command. The FinalizeQueue similar to DumpHeap provides the Method Table, Object Count, Size allocated and the Type.

  • Here 6dcf1230 is one of the Method Tables that I was interested in. I wanted to see if any objects in this table were rooted.
  • myvar is a variable that holds the output of the !DumpHeap command. –short returns only the address. Maybe in another article I will cover how to process the tokens
  • the .echo command needs no introduction, in this content it is used twice to display the address and to separate the output.
.foreach (myvar {!DumpHeap /d -mt 6dcf1230 -short}) {.echo myvar;!gcroot -all myvar;.echo **************;}

 

ServiceNow – Print/Export a Complex Visual Task Board

ServiceNow has a cool feature called Visual Task Board. It’s an implementation of a Kanban Board and you can read about it here. This makes life so much easier and the Team can pretty much manage the standup / 1:1 meetings. I have such board setup with 7 lanes and several 100 cards. End of the month, I wanted to save the contents of the board to a PDF for posterity. This is where it became a little complicated. The OOTB print function produces an output that wasn’t presentable.

The steps outlined in this document may change with ServiceNow’s implementation in a future release, but you get the idea.

What you need

  • Internet Explorer and it’s Developer Toolbar
  • SnagIt
  • Dual Monitor (for a large Dashboard)
  1. Open Internet Explorer and Navigate to your VTB. Expand it over as many monitors as you need so that you see all the content horizontally. We will work on a fix to get the vertical content in step 2.
  2. Open F12 Developer Tools from the Tools menu or Press F12. Follow these 2 steps to edit the body tag and adjust the height of the div tag as shown below.

01.png

02.png

3. We now have a window that shows all of the content. Capture the window using SnagIt or any other tool that supports scrolling at this point and capture the page. I then saved it png / pdf formats.

Hope this helps till ServiceNow implements a feature to export this Dashboard in its entirety.

Fun with WinDbg

I was looking into a Memory Leak  using a win-32 w3wp memory dump. Looking into the FinalizeQueue, I saw System.Data.DataTableCollection object in Gen2. Based on the hunch, I dumped the method table.

01

Dumped a couple of addresses listed above

02

I looked at the dataset and the _list, which brought me to the _items below.

03

The list of items is actually an Array.

04

Dumping the array and reviewing one of the array element(s) as below.

06

Viewing the value for tablename showed the string below. This is one of the objects used to retain Trace data.

07I am not posting the rest of the table structure, but if you are interested, you are welcome to follow the steps. Trace.axd was still active and ended up disabling it. Now back to looking into the actual issue.

Serialize a Class to XML

Sometimes it helps to serialize an object and include it into the log file in a QA build for posterity. Inherit from this class and you can use Enterprise Logger to log basic meaningful types to the log file.

using System.IO;
using System.Xml;

public class Serializer
{
    public override string ToString()
    {
        Serialization.XmlSerializer xmlSerializer = new Serialization.XmlSerializer(this.GetType());

        using (StringWriter TextWriter = new StringWriter()) {
            xmlSerializer.Serialize(TextWriter, this);
            return TextWriter.ToString();
        }
    }
}
using System.Runtime.Serialization;
using System.Collections.ObjectModel;

[Serializable()]
public class UserRole : Serializer
{
    public UserRole()
    {
    }
}

Now write the object using Enterprise Library Logging Framework, where role is in instance of UserRole

Logger.Write(role);

 

 

Network Error in Chrome while downloading files

The dev team reported an odd behavior that exhibited only in Chrome. Since MS Excel is a preferred tool to view transactional data for analysis/self-service, we have an Export function within web pages.

Using OpenXMLSDK to stream the data failed with a “Network Error” in chrome. Surprisingly the same functionality worked in Chrome when the website was hosted on IIS 8.5 / .NET 4.5.

I then traced the Request and Response in fiddler. Decoding the response threw the “chunked body did not terminate properly with 0-sized chunk” error. So, I suspected that the Response was being truncated.

Reviewing the .NET code, after writing the memory stream to the HTTP Response object,  we had a Response.Close. It looks like Response.Close drops the last chuck (in IIS prior IIS versions) causing Chrome to throw the network error. It doesn’t explain how Firefox and IE were able to keep the pipe open to receive the last chunk (need to dig into this further using wireshark).

Removed the Response.Close and all good now.