How do you find out why your computer or a running program is so slow? Here’s one way.

 

Let’s attach the VS debugger to VS itself. The main executable for VS is devenv.exe.

 

Start Visual Studio 2008. This will be the “debugger”

 

Choose File->Open Project    C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe

 

(You can also choose Debug->Attach to process to debug another instance of devenv.exe, or any other EXE, like Foxpro.exe or Excel.exe)

 

 

Hit F5, and a dialog pops up:

“Debugging information for ‘devenv.exe’ cannot be found or does not match. Symbols not loaded. Do you want to continue debugging?”. 

Answer yes, and Visual Studio starts. This will be the “debuggee”

 

Do anything you like in the debuggee, such as create or load a project. Hit F12 to cause an asynchronous breakpoint (or go to the debugger and choose Debug->Break All).

 

That will freeze all the debuggee threads and put you in the debugger.

 

You can then examine the Threads window (Debug->Windows->Threads) and see what threads are running.  There are several. You can dbl-click various threads and look at the Call stack for it (Debug->Windows->Call stack).

 

You’ll probably see that most threads are just waiting for something to happen.

 

Choose the main thread. When VS is idling, the stack will look like this:

 

            ntdll.dll!7c90eb94()       

            [Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]

            ntdll.dll!7c90e9ab()       

            kernel32.dll!7c8094e2()

>          msvcr90.dll!_onexit_nolock(int (void)* func=0x0072006f)  Line 157 + 0x6 bytes    C

            00660072()       

 

Each stack entry shows the module and address that called the next stack entry. This isn’t very useful, so you need to load symbols. You can use the public Microsoft Symbol Server:

 

Tools->Options->Debug->Symbols

 

http://msdl.microsoft.com/download/symbols

 

Cache the symbols to a local dir, like C:\Symbols

 

Right click on the various modules (like ntdll.dll, kernel32.dll, msenv.dll etc.) in the call stack to load symbols. Now it’s a little more intelligible:

 

>          ntdll.dll!_KiFastSystemCallRet@0()       

            user32.dll!_NtUserKillTimer@8()  + 0xc bytes     

            msenv.dll!CMsoCMHandler::FPushMessageLoop()  + 0x36 bytes           

            msenv.dll!SCM::FPushMessageLoop()  + 0x4f bytes     

            msenv.dll!SCM_MsoCompMgr::FPushMessageLoop()  + 0x28 bytes      

            msenv.dll!CMsoComponent::PushMsgLoop()  + 0x28 bytes       

            msenv.dll!VStudioMainLogged()  + 0x19b bytes

            msenv.dll!_VStudioMain()  + 0x7d bytes

            devenv.exe!util_CallVsMain()  + 0xd8 bytes       

            devenv.exe!CDevEnvAppId::Run()  + 0x5cb bytes         

            devenv.exe!_WinMain@16()  + 0x60 bytes         

            devenv.exe!License::GetPID()  - 0x4cf9 bytes    

            kernel32.dll!_BaseProcessStart@4()  + 0x23 bytes         

 

You can see the WinMain calls a MessageLoop.

 

 

Let’s make the foreground thread busy. Create a VB console application. Add an XML literal:

 

Module Module1

 

    Sub Main()

        Dim bigxml = <xml>

 

                  </xml>

    End Sub

 

End Module

 

Make the XML literal big: open the file C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.Linq.xml and copy everything except the <?xml version="1.0" encoding="utf-8"?> into the literal (between the <xml> and the </xml>), so you have about 2000 lines. 

 

Start Task Manager (Ctrl-Shift->Escape, or right click on the task bar and choose Task Manager). Observe the little tray icon in the System tray. It will indicate how busy your computer is.

 

For example, if you have 2 processors and 1 thread is very busy, it’ll show 50% busy.

 

Now, if you hover your mouse over the “bigxml”, you’ll trigger VB to create a Quick Info tooltip, but it takes quite a lot of calculation to figure out the tip.

 

Do your asynchronous breakpoint trick and you’ll see something like this:

 

>          msvb7.dll!BCSYM::IsNamedRoot()          

            msvb7.dll!BCSYM::AreTypesEqual()     

            msvb7.dll!EquivalentTypes()  + 0x17 bytes        

            msvb7.dll!ClassifyPredefinedCLRConversion()  + 0x22 bytes     

            msvb7.dll!Semantics::ClassifyPredefinedCLRConversion()  + 0x28 bytes

            msvb7.dll!Semantics::ClassifyPredefinedConversion()  + 0xbf bytes      

            msvb7.dll!Semantics::ResolveConversion()  + 0x234 bytes        

            msvb7.dll!Semantics::ClassifyUserDefinedConversion()  + 0x40685 bytes          

            msvb7.dll!Semantics::ClassifyConversion()  + 0x21e31 bytes     

            msvb7.dll!Semantics::CompareParameterTypeSpecificity()  + 0x57 bytes           

            msvb7.dll!Semantics::CompareParameterSpecificity()  + 0xa3 bytes       

            msvb7.dll!Semantics::InsertIfMethodAvailable()  + 0x461 bytes  

            msvb7.dll!Semantics::CollectOverloadCandidates()  + 0x1e6 bytes         

            msvb7.dll!Semantics::ResolveOverloading()  + 0xd9 bytes         

            msvb7.dll!Semantics::ResolveOverloadedCall()  + 0x5e bytes    

            msvb7.dll!Semantics::InterpretCallExpression()  + 0x2656f bytes

            msvb7.dll!Semantics::CreateConstructedInstance()  + 0xfa bytes

            msvb7.dll!Semantics::CreateConstructedInstance()  + 0x5b bytes           

            msvb7.dll!Semantics::InterpretXmlElement()  + 0x241 bytes       

            msvb7.dll!Semantics::InterpretXmlContent()  + 0x56 bytes          

            msvb7.dll!Semantics::InterpretXmlElement()  + 0x27c bytes       

            msvb7.dll!Semantics::InterpretXmlContent()  + 0x56 bytes          

            msvb7.dll!Semantics::InterpretXmlElement()  + 0x27c bytes       

            msvb7.dll!Semantics::InterpretXmlContent()  + 0x56 bytes          

            msvb7.dll!Semantics::InterpretXmlElement()  + 0x27c bytes       

            msvb7.dll!Semantics::InterpretXmlContent()  + 0x56 bytes          

            msvb7.dll!Semantics::InterpretXmlElement()  + 0x27c bytes       

            msvb7.dll!Semantics::InterpretXmlExpression()  - 0x13d bytes   

            msvb7.dll!Semantics::InterpretXmlExpression()  + 0xb0 bytes    

            msvb7.dll!Semantics::InterpretXmlExpression()  + 0xc3 bytes    

            msvb7.dll!Semantics::InterpretExpression()  - 0x1fc bytes          

            msvb7.dll!Semantics::InterpretExpressionWithTargetType()  + 0x43 bytes           

            msvb7.dll!Semantics::InterpretInitializer()  + 0x36 bytes  

            msvb7.dll!Semantics::InterpretInitializer()  + 0x1a bytes  

            msvb7.dll!Semantics::InterpretInitializer()  + 0x127 bytes 

            msvb7.dll!Semantics::InterpretVariableDeclarationStatement()  + 0x158c bytes    

            msvb7.dll!Semantics::InterpretStatement()  + 0x7b2f bytes         

            msvb7.dll!Semantics::InterpretStatementSequence()  + 0x2f bytes          

            msvb7.dll!Semantics::InterpretBlock()  + 0x24 bytes      

            msvb7.dll!Semantics::InterpretMethodBody()  + 0x1fa bytes      

            msvb7.dll!SourceFile::GetBoundMethodBodyTrees()  + 0x126 bytes       

            msvb7.dll!CBaseSymbolLocator::GetBoundMethodBody()  + 0xb6 bytes           

            msvb7.dll!CSymbolLocator::LocateSymbolInMethodImpl()  + 0x29 bytes

            msvb7.dll!CSymbolLocator::LocateSymbol()  + 0x81f bytes       

            msvb7.dll!CIntelliSense::GenQuickInfo()  + 0x52399 bytes          

            msvb7.dll!CIntelliSense::HandleQuickInfo()  + 0x29 bytes           

            msvb7.dll!CIntelliSense::ProcessCompletionInfo()  + 0x66cda bytes       

            msvb7.dll!CIntelliSense::GenIntelliSenseInfo()  + 0x402 bytes     

            msvb7.dll!SourceFileView::GenIntelliSenseInfo()  + 0x94 bytes   

            msvb7.dll!SourceFileView::GetDataTip()  + 0x743 bytes 

            msvb7.dll!CVBLangService::GetDataTip()  + 0x11d bytes           

            msenv.dll!CEditView::GetFilterDataTipText()  + 0x37 bytes         

            msenv.dll!CEditView::HandleHoverWaitTimer()  + 0x213 bytes    

            msenv.dll!CEditView::TimerTick()  + 0x7d bytes 

            msenv.dll!CEditView::WndProc()  + 0x1685b9 bytes      

            msenv.dll!CEditView::StaticWndProc()  + 0x39 bytes     

            user32.dll!_InternalCallWinProc@20()  + 0x28 bytes        

            user32.dll!_UserCallWinProcCheckWow@32()  + 0xb7 bytes       

            user32.dll!_DispatchMessageWorker@8()  + 0xdc bytes

            user32.dll!_DispatchMessageW@4()  + 0xf bytes          

            msenv.dll!EnvironmentMsgLoop()  + 0xb6 bytes

            msenv.dll!CMsoCMHandler::FPushMessageLoop()  + 0x36 bytes           

            msenv.dll!SCM::FPushMessageLoop()  + 0x4f bytes     

            msenv.dll!SCM_MsoCompMgr::FPushMessageLoop()  + 0x28 bytes      

            msenv.dll!CMsoComponent::PushMsgLoop()  + 0x28 bytes       

            msenv.dll!VStudioMainLogged()  + 0x19b bytes

            msenv.dll!_VStudioMain()  + 0x7d bytes

            devenv.exe!util_CallVsMain()  + 0xd8 bytes       

            devenv.exe!CDevEnvAppId::Run()  + 0x5cb bytes         

            devenv.exe!_WinMain@16()  + 0x60 bytes         

            devenv.exe!License::GetPID()  - 0x4cf9 bytes    

            kernel32.dll!_BaseProcessStart@4()  + 0x23 bytes         

 

Note how it’s easy to read the stack. The bottom of the stack (the first thing put on it) is _BaseProcessStart, which starts the process. You can see that a timer tick caused GetDataTip to call GenIntelliSenseInfo, which calls GenQuickInfo, -> LocateSymbol, etc.

 

Each stack entry indicates the name of the routine being executed. The “+ 0x23 bytes” means the # of bytes into the routine that the call occurred. A low number means near the beginning of that method.

 

Because XML is a tree, and is thus a recursive data structure, you see that XMLContent can contain an XMLExpression and vice versa. The depth of the recursion reflects the actual XML being processed.

 

 

BTW, the VB background compiler thread is:

 

0          >          4620     Worker Thread   ThreadSyncManager::ThreadProc           _KiFastSystemCallRet@0            Normal  0

 

And it’s stack at idle:

 

>          ntdll.dll!_KiFastSystemCallRet@0()       

            ntdll.dll!_ZwWaitForMultipleObjects@20()  + 0xc bytes   

            kernel32.dll!_WaitForMultipleObjectsEx@20()  - 0x48 bytes        

            user32.dll!_RealMsgWaitForMultipleObjectsEx@20()  + 0xd9 bytes        

            ole32.dll!CCliModalLoop::BlockFn()  + 0x76 bytes         

            ole32.dll!_CoWaitForMultipleHandles@20()  + 0xe6 bytes           

            msvb7.dll!ThreadSyncManager::ThreadProc()  + 0x98 bytes       

            kernel32.dll!_BaseThreadStart@8()  + 0x37 bytes           

 

 

 

See also:

Dynamically attaching a debugger

Is a process hijacking your machine?