Course contentsShow
← C#
Lesson 1811 of 1,90816. Performance and DiagnosticsFree lesson

Analyzing Native Dependencies with SOS

Learners will use the SOS debugger extension to inspect the managed heap and native call stacks.

In this lesson, you will learn how to bridge the gap between managed C# code and native dependencies by using the SOS debugger extension to inspect the underlying call stacks and memory state.

When your application invokes native libraries—typically through P/Invoke or COM Interop—the standard managed debugger often loses visibility once the execution enters unmanaged memory. When an access violation occurs or performance degrades in a native module, you need sos.dll to peer through the transition layer.

Bridging the Managed-Native Divide

The SOS extension is built into modern versions of the .NET SDK. To begin your analysis, load SOS in your debugger (like WinDbg or the Visual Studio Debugger Immediate Window) using .load sos.

If you suspect a performance bottleneck or a crash is occurring within a native dependency, the first step is to capture the full call stack, including the native frames. In your debugger, use:

!eestack -ee

This command displays the managed call stack and includes native frames where transitions occurred. If you need to focus specifically on what the native side is doing, you can use standard debugger commands like k (for stack backtrace) while the debugger is positioned in an unmanaged frame.

Inspecting Native Memory

When dealing with native dependencies, you often face issues with heap corruption or memory leaks outside of the managed GC’s purview. You can use SOS to verify if your managed objects are correctly pinning native pointers, or to inspect the transition state:

  1. Use !clrstack to see your current managed method.
  2. If the code is stalled in a native call, the stack will show the native transition.
  3. Use !dumpstackobjects to see which managed objects are currently on the stack, which helps identify if the native call is holding a reference to a managed object that should have been collected.

By identifying the exact native address where a thread is blocked, you can map that address back to the specific native library using lm (list modules) to confirm if your application is stuck in a specific third-party DLL.

Key Takeaway: SOS allows you to traverse the boundary between managed code and native dependencies, providing the visibility needed to debug crashes and blocking operations that occur outside the .NET runtime’s managed heap.