Monday, June 7, 2010

Windbg Ignoring Sympath when called programatically

Working with mdbglib this weekend my call to .sympath was registering but then none of the symbol files could be found.  This post will show how to resolve this issue and the process I followed to solve it.

Environment Windows Server R2 64 bit.  The app is compiled for x86 and running as a 32 bit app.

  1. lm e
    This listed modules which had errors.
  2. reload -f
    Force a reload of all modules.  Noticed none of them were loading from my path.
  3. Hooked up SysInternals Process Monitor filtered to just my process name and just disk activity.  cleared the output right before doing reload -f
    NOticed it was attempting to load modules from the same directory I was running from.
  4. restarted the application
  5. !sym noisy
    To noisy load symbols.
  6. .reload

    DBGHELP: SymSrv load failure: symsrv.dll
    DBGHELP: wntdll.pdb - file not found
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\SysWOW64\ntdll.dll - 
    DBGHELP: ntdll - export symbols
    DBGHELP: wuser32.pdb - file not found
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\syswow64\USER32.dll - 
    DBGHELP: USER32 - export symbols
    DBGHELP: System.Windows.Forms.pdb - file not found
    *** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v2.0.50727_32\System.Windows.Forms\fedf1ba58dced4f0b3f8c457648ceed9\System.Windows.Forms.ni.dll
    *** ERROR: Module load completed but symbols could not be loaded for C:\Windows\assembly\NativeImages_v2.0.50727_32\System.Windows.Forms\fedf1ba58dced4f0b3f8c457648ceed9\System.Windows.Forms.ni.dll
    DBGHELP: System_Windows_Forms_ni - no symbols loaded

  7. Copied symsrv.dll into my local directory even though it is already in the app path.
  8. Restarted, !sym noisy, .reload
    same results
  9. Filter Process monitor where the path ends in symsrv.dll
    Noticed the process is trying to load symsrv.dll from C:\Windows\SysWOW64\
  10. Copied symsrv.dll to C:\Windows\SysWOW64
    HACK!
  11. restarted, !sym noisy, .reload
    success!
  12. .chain
    Shows dbghelp.dll is loaded from c:\Windows\system32\dbghelp.dll
    MSDN writes symsrv must be installed in the same directory as the copy of dbghelp.dll being loaded.
  13. .extpath C:\Program Files (x86)\Debugging Tools for Windows (x86)\
    sets dbgeng's extension search path to where symsrv.dll is.
  14. .chain
    dbghelp.dll is still loaded from the system32 directory.
  15. Prior to running the debugger I set the environmental variable _NT_DEBUGGER_EXTENSION_PATH to C:\Program Files (x86)\Debugging Tools for Windows (x86)\
  16. Restart, .chain
    dbghelp is loaded from the correct directory! but symsrv.dll is still broken :(
    Looking at the stack in Process monitor shows dbgeng.dll is being loaded from C:\Windows\SysWOW64\dbgeng.dll which calls dbghelp.dll in the same directory.
    Changing process monitor to filter for requests ending in dbgeng.dll shows when the program runs it is looking in the executing applications directory for the dll, not finding it, and then loading it from C:\Windows\SysWOW64\dbgeng.dll
  17. Head over to pinvoke.net to grab the loadlibrary signature and put it in my application.

    [DllImport("kernel32", SetLastError = true)]
    static extern IntPtr LoadLibrary(string lpFileName);

  18. Call LoadLibrary from C# prior to loading my debugger and now the correct dll is bing loaded, but the debugging commands don't return any values :(
  19. Reading the LoadLibrary documentation and then the dll search order documentation I think calling SetDllDirectory may do what I want.
  20. Back to pinvoke.net to grab this signature
    [DllImport("kernel32.dll", SetLastError=true)]
    static extern bool SetDllDirectory(string lpPathName); 
  21. Call SetDllDirectory prior to loading up the debugger and everything now works as expected!

Here is the code:

internal static class UnsafeNativeMethods
{
    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern bool SetDllDirectory(string lpPathName);
}



public static class Utility
{
    public static void ProvideWindbgInstallationDirectory(string path)
    {
        if (Directory.Exists(path) == false)
        {
            throw new DirectoryNotFoundException(path);
        }
        #warning add a check that the files we want exist.  maybe even that they are the correct version?
        bool setPath = UnsafeNativeMethods.SetDllDirectory(path);
        if (setPath == false)
        {
            int error = Marshal.GetLastWin32Error();
            throw new InvalidOperationException("Setting the installation directory failed with error code: " + error.ToString());

        }
    }
}