3

I am trying to debug a memory leak in a large application. My first step is to familiarise myself with WinDBG. I have the following program in .NET:

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Try
        Dim HelloWorld As String = "HelloWorld"
        Dim testInt As Integer
        Me.Show()
        For testInt = 0 To 1000000
            TextBox1.Text = testInt 'There is a textbox on the form
            Application.DoEvents() 'So textbox updates on the fly
        Next

    Catch ex As Exception

    End Try
End Sub

I am trying to view the content of the HelloWorld variable in WinDbg, which should be "HelloWorld". I have followed these instructions:

1) Attached WinDBG to the executeable and start the debugger.

2) Break immediately (before loop finishes)

3) loadby sos clr

4) ~0s

5) !clrstack

6) !dumpheap -type WinDBG (the project in called WinDBGApplication)

The output is this:

Statistics:
      MT    Count    TotalSize Class Name
00186f88        1           12     WinDBGTest.My.MyProject+ThreadSafeObjectProvider`1[[WinDBGTest.My.MyProject+MyWebServices, WinDBGTest]]
00186e80        1           12 WinDBGTest.My.MyProject+ThreadSafeObjectProvider`1[[WinDBGTest.My.MyProject+MyForms, WinDBGTest]]
00186e2c        1           12 WinDBGTest.My.MyProject+MyForms
00186d3c        1           12 WinDBGTest.My.MyProject+ThreadSafeObjectProvider`1[[Microsoft.VisualBasic.ApplicationServices.User, Microsoft.VisualBasic]]
00186cec        1           12 WinDBGTest.My.MyProject+ThreadSafeObjectProvider`1[[WinDBGTest.My.MyComputer, WinDBGTest]]
00186be8        1           12 WinDBGTest.My.MyProject+ThreadSafeObjectProvider`1[[WinDBGTest.My.MyApplication, WinDBGTest]]
00184bf4        1          104 WinDBGTest.My.MyApplication
00187308        1          328 WinDBGTest.Form1
Total 8 objects

There is no evidence of a String called HelloWorld and based on my limited understanding of low level debugging there should be. What am I doing wrong?

I have tried what was suggested by Rockstart and get this output:

OS Thread Id: 0x7a0 (0)
ESP/REG  Object   Name
0039E5EC 0234db94 System.Windows.Forms.Control+ControlNativeWindow
0039E63C 0234dadc System.Windows.Forms.TextBox
0039E658 0234dadc System.Windows.Forms.TextBox
0039E668 0234dadc System.Windows.Forms.TextBox
0039E680 0234db94 System.Windows.Forms.Control+ControlNativeWindow
0039E698 0234db94 System.Windows.Forms.Control+ControlNativeWindow
0039E6A8 0234db94 System.Windows.Forms.Control+ControlNativeWindow
0039E7FC 0234dadc System.Windows.Forms.TextBox
0039E864 02604b10 System.Windows.Forms.Control+MultithreadSafeCallScope
0039E868 02604b68 System.String    26309
0039E878 026049a8 System.String    26310
0039E87C 0234dadc System.Windows.Forms.TextBox
0039E880 026049a8 System.String    26310
0039E884 0234db94 System.Windows.Forms.Control+ControlNativeWindow
0039E890 0234db94 System.Windows.Forms.Control+ControlNativeWindow
0039E8A0 026049a8 System.String    26310
0039E8AC 0234dadc System.Windows.Forms.TextBox
0039E8B4 0234db84 System.Windows.Forms.PropertyStore
0039E8C8 0234dadc System.Windows.Forms.TextBox
0039E8CC 026049a8 System.String    26310
0039E8DC 026049a8 System.String    26310
0039E8E0 0234dadc System.Windows.Forms.TextBox
0039E8EC 0234dadc System.Windows.Forms.TextBox
0039E8F0 0234dadc System.Windows.Forms.TextBox
0039E904 0234d4e4 WinDBGTest.Form1
0039E940 0234d4e4 WinDBGTest.Form1
0039E944 0234e4c0 System.EventHandler
0039E970 0234a1c4 System.EventArgs
0039E978 0234d4e4 WinDBGTest.Form1
0039E9DC 0234d4e4 WinDBGTest.Form1
0039EA00 0234d4e4 WinDBGTest.Form1
0039EAE0 0234d984 System.Windows.Forms.Control+ControlNativeWindow
0039ED28 0234d4e4 WinDBGTest.Form1
0039ED34 0234d4e4 WinDBGTest.Form1
0039ED3C 0234d4e4 WinDBGTest.Form1
0039ED44 0234d4e4 WinDBGTest.Form1
0039ED4C 0234d4e4 WinDBGTest.Form1
0039EDCC 0234d4e4 WinDBGTest.Form1
0039EE18 023491c8 System.Windows.Forms.Application+ThreadContext
0039EE58 023490c8 Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase+WinFormsAppContext
0039EE8C 02348f54 WinDBGTest.My.MyApplication
0039EEA8 02348f54 WinDBGTest.My.MyApplication
0039EEB8 02348f54 WinDBGTest.My.MyApplication
0039F1D8 02348640 System.Object[]    (System.String[])
Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
w0051977
  • 15,099
  • 32
  • 152
  • 329
  • It is not entirely impossible, but windbg is a pretty crappy tool to troubleshoot leaks. It does *very* little to help you avoid drowning in the mountain of data, the noise-to-signal ratio is very poor. Use a .NET memory profiler instead. – Hans Passant Mar 25 '12 at 16:56
  • I would agree, but WinDbg is a free. Do you know the command I can try to view the contents of this String? – w0051977 Mar 25 '12 at 17:10

3 Answers3

0

The variable Helloworld would have been stored in stack. Hence use,!dso to dump the stack objects. You should be able to see Helloworld string

Rockstart
  • 2,337
  • 5
  • 30
  • 59
  • I have tried what you suggested. How can I identify which String from the list relates to: HelloWorld. – w0051977 Mar 25 '12 at 17:09
  • 1
    Try !sosex.mdso /t:System.String. This will dump out only the strings and will also list the string values. Sosex is free at http://www.stevestechspot.com. You can also do this with !sos.dso, but it's a bit more work. Start at the top of the stack dump, probably in the registers. Since you're broken in the stack frame where the HelloWorld string lives, it should be very near the top. – Steve Johnson Mar 26 '12 at 00:00
0

!dumpheap -type WinDBG is not the correct approach here, if you're looking for a string. If you want to use !dumpheap for this, you need to search for String, as that is the name of the type. The fact that the string is referenced from a method within a namespace called WinDBGApplication isn't relevant here. However, dumping all strings will give you a lot of references even for a small application like this as the runtime allocates a bunch of strings, so that isn't very useful either.

The string instance in question is referenced by a local variable, so the best way to find it is to do a !dso as suggested by Rockstart. This will list all the references on the current stack and since the string is referenced by this stack, you'll find the address here. To dump the string itself use !do with the address of the string as displayed in the output from !dso.

Also, please take a look at this question.

Community
  • 1
  • 1
Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
0

"2) Break immediately (before loop finishes)"

How are you ensuring that you break before the loop finishes? Are you keeping any breakpoints through windbg? If this breaking is not done at the right place, then you may not get the string in stack.

I tried a similar program in my desktop and I could see the string. Here is my sample program.

class Program
{
    static void Main(string[] args)
    {
        var testclass = new testClass();
        testclass.Load();
    }


}

public class testClass
{
    public void Load()
    {
        var HelloWorld = "Hello World!!";
        string temp = string.Empty;
        for (int i = 0; i < 1000; i++)
        {
            temp = i.ToString();

            if (i == 400)
            {
                Console.ReadKey();
            }
        }
    }
}

}

When the control reached Console.ReadKey(), I attached the debugger and ran !dso. Here is the result.

enter image description here

One another approach , can you remove "Me.Show()" and try the same again. I fear, Me.Show will spawn in the main thread and push your logic to worker thread and hence you are not able to see the string in the stack.

Rockstart
  • 2,337
  • 5
  • 30
  • 59