Click here to Skip to main content
15,885,914 members
Articles / Programming Languages / C#

Getting info from .NET applications in production environments

Rate me:
Please Sign up or sign in to vote.
4.89/5 (9 votes)
22 Dec 2012CPOL4 min read 33.5K   1.2K   61   7
Get simple debug info from a tray icon.

Introduction   

New: appCompanion now has a codeplex page (contirbutions are welcome). 

This small mdbg based program helps you get some inside information about the working of your program in a client's (non dev) environment, without installing VisualStudio or learning how to use windbg. 

Using this you can:    

  • Get an application callstack from all threads, on the fly. 
  • Take a small performance snapshot, showing you where the time is beeing wasted. 
  • View the system's debug console (dbgView) 
  • Listen to exceptions (first + second time) 
  • Take hang dumps for the process. 

This tool can help in more ways than are initialy visible, for example getting callstacks can help in spotting where in the code an unknown flow runs, by opening a dialog and finding out its name in the callstack, or by diagnosing a hanged process as running with a hidden dialog behind the main app.

[Download demo project] [Download source]  

In the making of this program I also used Chritian-Birkl's excellent CodeProject article about replicating DebugView's abilities with C#.

32/64 bit support   

the first versio of this article was compiled as x86, which limited it's scope. This is now fixed:
  • Since the debugger is platform specific, you can't use a x64 debugger to debug a x86 process and vice versa. 
  • AnyCPU will be run as x64 on a x64 OS and as x86 on a x86 OS. 
  • To use with AnyCPU/x64  apps, you need to compile AppCompanion using AnyCPU. 
  • For x86 applications compile AppCompanion using x86 in the exe project properties. 
  • The demo zip now includes both versions.  
  • When trying to attach to a process with an incorrect platform, the AppCompanion will show an error baloon and exit. 

Background    

After several years of using windbg+sos to get debug info from code running in preprod and production environments, I discovered MSE, only to lose it again when .NET 4.0 came and it was obsolete. (today there are several alternatives)  

Lately i have been thinking that i can use mdbg to create something that can help me track my app, which can give a bit more than MSE (though less than windbg), in an elegant way.  

I tried to gather some helpful functions for Dev (and QA personnel as well) to use for getting more data when the application isn't providing enough. 

Using the AppCompanion demo 

Edit the AppCompanion\AppStateMonitor.exe.config file, change myProgram to your program name, without the ".exe" extention. 

  • when running the AppCompanion,  it will create a small tray icon (you might need to customize windows tray to show it) 
  • when it identifies the target application, it will "steal" it's icon and use it as it's own. 
  • A right click on this icon gives you all the options described in the first paragraph of this article 

About the source

Making mdbg work  

I used Microsoft's mdbg 4.0 sample, to attach and get info from the process. Since the debugger requires a MTA thread, and graphics require STA threads. To go around this conflict, I used TPL Tasks to run the debugger code, and since TPL threads (and basically all managed threads) are MTA by default, this did the trick. 

I'm still toying around with the idea of putting the debugger code in a single thread with a custom message pump using a blocking collection, but can't find a really good reason to... 

C#
//attaching
Task t = Task.Factory.StartNew(() =>
        {
            MDbgEngine debugger = new MDbgEngine();
            MDbgProcess process = DebuggerUtils.AttachToProcess(m_processId, debugger);
            process.CorProcess.Stop(0);
            process.Detach();
        });
        return t;

//detaching
Task t = Task.Factory.StartNew(() =>
                {
                    if (m_process.IsAlive)
                    {
                        m_process.CorProcess.Stop(0);
                        m_process.Detach();
                    }
                });
            t.Wait();

Taking performace samples 

The sampling/performace code, works the same way some profilers work when sampling:

  • Take a snapshot every x miliseconds
  • Merge the samples to a call tree 
  • Increment each node according to the number of times this call was seen. 

This gives you a good sense of how much time was spent on each node, and can easily be multiplied by the number of miliseconds between samples to get a good idea of the actual time.

Debugger console

I added this since I found it usefull for my own purposes, the code isn't mine but Chritian-Birkl's as stated above. 

Taking hang/crash dumps   

Dumps are taken using a pinvok to MiniDumpWriteDump function in the dbghelp.dll:  

C#
 // Overload supporting MiniDumpExceptionInformation == NULL
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", 
   CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, 
  uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
static extern uint GetCurrentThreadId();
public static bool WriteDump(int pid, int threadWithExceptionId, 
       string filename, DumpOptions options, bool hasException)
{
    using (FileStream dumpFile = File.OpenWrite(filename))
    {
        SafeHandle fileHandle = dumpFile.SafeFileHandle;
        Process currentProcess = Process.GetProcessById(pid);
        IntPtr currentProcessHandle = currentProcess.Handle;
        uint currentProcessId = (uint)currentProcess.Id;
        MiniDumpExceptionInformation exp;
        exp.ThreadId = threadWithExceptionId;//GetCurrentThreadId();
        exp.ClientPointers = false;
        exp.ExceptionPointers = IntPtr.Zero;
        if (hasException)
        {
            exp.ExceptionPointers = System.Runtime.InteropServices.Marshal.GetExceptionPointers();
        }
        bool bRet = false;
        if (exp.ExceptionPointers == IntPtr.Zero)
        {
            bRet = MiniDumpWriteDump(currentProcessHandle, currentProcessId, 
              fileHandle, (uint)options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
        }
        else
        {
            bRet = MiniDumpWriteDump(currentProcessHandle, currentProcessId, 
              fileHandle, (uint)options, ref exp, IntPtr.Zero, IntPtr.Zero);
        }
        return bRet;
    }
}
  • Dumps can be taken in any time, not depending on debugger activity. 
  • The application will snap a dump on uncaught exception while listening for exceptions and ProcessListener.WriteDumpOnUncaughtExceptions is set to true (false by default). 

Potential future feaures and improvements  

  • Adding the ability to create breakpoints
  • Code evaluation when stopped in breakpoints 
  • Sampling with a moving window/untill stopped instead of a constant time.

I hope you find this usefull as a tool or as a code sample for the use of mdbg.  

I might consider making this an open source project if there is a strong demand, but for now it's just something i mocked up in my spare time. =) 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Hp Software
Israel Israel
I've been all over the coding world since earning my degrees
have worked in c++ and java, finally setteling into c# about 6 years ago, where i spent a good amount of my time in Performance tweaking & memory debugging, as well as designing new solutions and hacking at old ones to stay in line.

Computers never cease to amaze me, and i'm glad to have found a field where i get paid to do what i enjoy.

I have been toying around with the idea of publishing stuff online for years, never actually getting around to it, so i still have a lot of stuff to write up, aside from all the other new stuff i'll get excited about, hope you'll like enjoy reading it as much as i enjoy writing.

linkedin
google plus

Comments and Discussions

 
GeneralMy vote of 5 Pin
wvd_vegt10-Dec-12 4:58
professionalwvd_vegt10-Dec-12 4:58 
GeneralRe: My vote of 5 Pin
Amit Bezalel10-Dec-12 7:00
Amit Bezalel10-Dec-12 7:00 
GeneralRe: My vote of 5 Pin
wvd_vegt16-Dec-12 23:42
professionalwvd_vegt16-Dec-12 23:42 
GeneralRe: My vote of 5 Pin
Amit Bezalel17-Dec-12 4:40
Amit Bezalel17-Dec-12 4:40 
GeneralMy Vote of 5 Pin
oldsage16-Dec-12 19:21
oldsage16-Dec-12 19:21 
GeneralMy vote of 5 Pin
PraveenKumarReddyChinta5-Dec-12 23:58
PraveenKumarReddyChinta5-Dec-12 23:58 
GeneralMy vote of 5 Pin
Dominic Abraham5-Dec-12 22:16
Dominic Abraham5-Dec-12 22:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.