The Black Box of .NET Headline Animator

Showing posts with label debug. Show all posts
Showing posts with label debug. Show all posts

December 24, 2024

Memory Leaks - Part 2: Debugging Tools to Diagnose a Memory Leak

If you are reading this you may be in the unfortunate position of looking for an elusive and possibly intermittent problem in your production application. Debugging and diagnosing a memory leak or an OutOfMemoryException can be a daunting, intimidating and challenging task. Fortunately, there are a number of tools to help; some of these are "paid license" apps, but there are even more tools that are free. I'll discuss some .NET Debugging Tools available. I've used all of the following tools with the exception of SciTech .NET Memory Profiler. Each has its advantages and disadvantages. Personally, I prefer the free tools for several reasons:

  1. Getting approval for licensing and purchase of a 3rd-party tool is an uphill task and you rarely have time to wait for the approval process when it is a production issue.
  2. I find the feature set of a combination of the free tools gives you the largest "surface area" of features to help. Some are better at strict data collection, others have graphical views, some are static for post-mortem debugging, and others can do real-time analysis.
  3. Even though these are free, they are very robust and provide just as much data as the paid license tools. A good example of this is WinDbg which was developed in 1993 by Microsoft for in-house debugging of the Windows Kernel.

Free Tools

Advantages

  • Duh! It's free
  • There are lots of tools to choose from
  • All of the ones I've seen are from reputable and well-known companies.
  • You can often find blog posts (ahem...), articles, reddit threads, etc. that can provide some direction for getting started.

Disadvantages

  • Formal documentation can be lacking. Finding a blog post or article is great but comprehensive detail is often missing or at best glossed over. This can make getting started a bit more of a challenge if the tool is new to you.
  • Your company may have restrictions on using free tools for fear of malware, liability, or other reasons.

Licensed Tools

Hope this helps!


Share

December 8, 2011

How to Tell if an Assembly is Debug or Release

The DebuggableAttribute is present if you compile in any setting for 'Debug' mode and when Release mode is selected and Debug Info set to anything other than "none". So, just looking for the presence of DebuggableAttribute is not sufficient and could be misleading. So, you could still have an assembly that is JIT optimized where the DebuggableAttribute is present in the Assembly Manifest.

First, you need to define exactly what is meant by "Debug" vs. "Release"...
  • Do you mean that the app is configured with code optimization?
  • Do you mean that you can attach the VS/JIT Debugger to it?
  • Do you mean that it generates DebugOutput?
  • Do you mean that it defines the DEBUG constant?  Remember that you can conditionally compile Methods with the System.Diagnostics.Conditional() attribute.
IMHO, when someone asks whether or not an assembly is "Debug" or "Release", they really mean if the code is optimized...

Sooo, do you want to do this manually or programmatically?

Manually:
You need to view the value of the DebuggableAttribute bitmask for the assembly's metadata.  Here's how to do it:

  1. Open the assembly in ILDASM
  2. Open the Manifest
  3. Look at the DebuggableAttribute bitmask.  If the DebuggableAttribute is not present, it is definitely an Optimized assembly.
  4. If it is present, look at the 4th byte - if it is a '0' it is JIT Optimized - anything else, it is not:
// Metadata version: v4.0.30319
....
     //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )

    Programmatically: assuming that you want to know programmatically if the code is JITOptimized, here is the correct implementation:

    void Main()
    {
    	var HasDebuggableAttribute = false;
    	var IsJITOptimized = false;
    	var IsJITTrackingEnabled = false;
    	var BuildType = "";
    	var DebugOutput = "";
    	var ReflectedAssembly = Assembly.LoadFile(@"C:\src\TMDE\Git\RedisScalingTest\bin\Release\netcoreapp3.1\RedisScalingTest.dll");
     
    	//	var ReflectedAssembly = Assembly.LoadFile(@"path to the dll you are testing");
    	object[] attribs = ReflectedAssembly.GetCustomAttributes(typeof(DebuggableAttribute), false);
     
    	// If the 'DebuggableAttribute' is not found then it is definitely an OPTIMIZED build
    	if (attribs.Length > 0)
    	{
    		// Just because the 'DebuggableAttribute' is found doesn't necessarily mean
    		// it's a DEBUG build; we have to check the JIT Optimization flag
    		// i.e. it could have the "generate PDB" checked but have JIT Optimization enabled
    		DebuggableAttribute debuggableAttribute = attribs[0] as DebuggableAttribute;
    		if (debuggableAttribute != null)
    		{
    			HasDebuggableAttribute = true;
    			IsJITOptimized = !debuggableAttribute.IsJITOptimizerDisabled;
     
    			// IsJITTrackingEnabled - Gets a value that indicates whether the runtime will track information during code generation for the debugger.
    			IsJITTrackingEnabled = debuggableAttribute.IsJITTrackingEnabled;
    			BuildType = debuggableAttribute.IsJITOptimizerDisabled ? "Debug" : "Release";
     
    			// check for Debug Output "full" or "pdb-only"
    			DebugOutput = (debuggableAttribute.DebuggingFlags &
    							DebuggableAttribute.DebuggingModes.Default) !=
    							DebuggableAttribute.DebuggingModes.None
    							? "Full" : "pdb-only";
    		}
    	}
    	else
    	{
    		IsJITOptimized = true;
    		BuildType = "Release";
    	}
     
    	Console.WriteLine($"{nameof(HasDebuggableAttribute)}{HasDebuggableAttribute}");
    	Console.WriteLine($"{nameof(IsJITOptimized)}{IsJITOptimized}");
    	Console.WriteLine($"{nameof(IsJITTrackingEnabled)}{IsJITTrackingEnabled}");
    	Console.WriteLine($"{nameof(BuildType)}{BuildType}");
    	Console.WriteLine($"{nameof(DebugOutput)}{DebugOutput}");
    }



    Share