views:

787

answers:

3

I want to use the MiniDumpWriteDump function to create some custom dump files (mainly, i want to export a dump file that contains the minimum amount of information for the thread callstacks), but i am having difficulties defining the structures that need to be passed as a parameter to the callback function

[StructLayout(LayoutKind.Explicit)]
internal struct MINIDUMP_CALLBACK_OUTPUT
{
    [FieldOffset(0)]
    public ulong ModuleWriteFlags;
    [FieldOffset(0)]
    public ulong ThreadWriteFlags;
}

public struct MINIDUMP_CALLBACK_INFORMATION
 {
  public IntPtr CallbackRoutine;
  public IntPtr CallbackParam;
 }

public delegate bool MINIDUMP_CALLBACK_ROUTINE(
  IntPtr CallBackParam, 
  MINIDUMP_CALLBACK_INPUT input, 
  MINIDUMP_CALLBACK_OUTPUT output);

[DllImport("dbghelp.dll")]
public static extern bool MiniDumpWriteDump(IntPtr hProcess, Int32 ProcessId, IntPtr hFile, int DumpType,
 IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallStackParam);

And the call looks like this:

MINIDUMP_CALLBACK_INFORMATION mci;
MINIDUMP_CALLBACK_ROUTINE r = new MINIDUMP_CALLBACK_ROUTINE(MyCallback);
GC.KeepAlive(r);
mci.CallbackRoutine = Marshal.GetFunctionPointerForDelegate(r);
mci.CallbackParam = IntPtr.Zero;    
IntPtr structPointer = Marshal.AllocHGlobal(Marshal.SizeOf(mci));    
Marshal.StructureToPtr(mci, structPointer, true);    
MiniDumpWriteDump(process[0].Handle, process[0].Id,
         fs.SafeFileHandle.DangerousGetHandle(),
         (int)MINIDUMP_TYPE.MiniDumpNormal,
         Marshal.GetExceptionPointers(),
         IntPtr.Zero,
         structPointer);

Marshal.FreeHGlobal(structPointer);

So my question is how to define MINIDUMP_CALLBACK_INPUT:

The definition of the structures in C that pose problems are:

typedef struct _MINIDUMP_CALLBACK_INPUT 
{
    ULONG                       ProcessId;
    HANDLE                      ProcessHandle;
    ULONG                       CallbackType;
    union 
    {
        MINIDUMP_THREAD_CALLBACK        Thread;
        MINIDUMP_THREAD_EX_CALLBACK     ThreadEx;
        MINIDUMP_MODULE_CALLBACK        Module;
        MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
        MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
    } u;
} MINIDUMP_CALLBACK_INPUT

typedef struct _MINIDUMP_MODULE_CALLBACK 
{
    PWCHAR                      FullPath;
    ULONGLONG                   BaseOfImage;
    ULONG                       SizeOfImage;
    ULONG                       CheckSum;
    ULONG                       TimeDateStamp;
    VS_FIXEDFILEINFO            VersionInfo;
    PVOID                       CvRecord;
    ULONG                       SizeOfCvRecord;
    PVOID                       MiscRecord;
    ULONG                       SizeOfMiscRecord;
} MINIDUMP_MODULE_CALLBACK
+1  A: 

It's not a direct answer of your question but a workaround...

Do you know the ClrDump lib which does what you need ? I've used it for a project several years ago and it works fine for me.


Answer to author comment:

Read on the site:

ClrDump can produce small minidumps that contain enough information to recover the call stacks of all threads in the application.

I wrapped the API in the following class:

internal class ClrDump
{
  [return: MarshalAs(UnmanagedType.Bool)]
  [DllImport("clrdump.dll", CharSet=CharSet.Unicode, SetLastError=true)]
  public static extern bool CreateDump(uint ProcessId, string FileName, MINIDUMP_TYPE DumpType, uint ExcThreadId, IntPtr ExtPtrs);

  [return: MarshalAs(UnmanagedType.Bool)]
  [DllImport("clrdump.dll", CharSet=CharSet.Unicode, SetLastError=true)]
  public static extern bool RegisterFilter(string FileName, MINIDUMP_TYPE DumpType);

  [DllImport("clrdump.dll")]
  public static extern FILTER_OPTIONS SetFilterOptions(FILTER_OPTIONS Options);

  [return: MarshalAs(UnmanagedType.Bool)]
  [DllImport("clrdump.dll", SetLastError=true)]
  public static extern bool UnregisterFilter();


  [Flags]
  public enum FILTER_OPTIONS
  {
    CLRDMP_OPT_CALLDEFAULTHANDLER = 1
  }

  [Flags]
  public enum MINIDUMP_TYPE
  {
    MiniDumpFilterMemory = 8,
    MiniDumpFilterModulePaths = 0x80,
    MiniDumpNormal = 0,
    MiniDumpScanMemory = 0x10,
    MiniDumpWithCodeSegs = 0x2000,
    MiniDumpWithDataSegs = 1,
    MiniDumpWithFullMemory = 2,
    MiniDumpWithFullMemoryInfo = 0x800,
    MiniDumpWithHandleData = 4,
    MiniDumpWithIndirectlyReferencedMemory = 0x40,
    MiniDumpWithoutManagedState = 0x4000,
    MiniDumpWithoutOptionalData = 0x400,
    MiniDumpWithPrivateReadWriteMemory = 0x200,
    MiniDumpWithProcessThreadData = 0x100,
    MiniDumpWithThreadInfo = 0x1000,
    MiniDumpWithUnloadedModules = 0x20
  }
}

and then, somewhere in my initialization code, I called the method RegisterFilter which registers an internal filter for unhandled exceptions in the current process. If the process crashes with unhandled exception (it can be native or managed exception), the filter catches it and creates a minidump (with the specified file name). Here is a sample code for this:

StringBuilder sb = new StringBuilder();
sb.Append(Path.GetFileNameWithoutExtension(Application.ExecutablePath));
sb.Append("_");
sb.Append(DateTime.Now.ToString("yyyyMMddHHmmssFF"));
sb.Append(".dmp");
string dmpFilePath = Path.Combine(Path.GetTempPath(), sb.ToString());
ClrDump.RegisterFilter(_dmpFilePath, ClrDump.MINIDUMP_TYPE.MiniDumpNormal);

You can read this article to understand the different MINIDUMP_TYPE options, but I think the basic one (MiniDumpNormal) could fit your needs.

cedrou
Yes, i read a little about clrdump lib, but i am not clear how i can get what i want with it. How can i get the minimum amount of information for the thread callstacks? Using filters? Could you give me a code sample?
anchandra
A: 

I take it that it is the union that is giving you trouble?

If CallbackType==KernelMinidumpStatusCallback, then the CALLBACK_INPUT structure is defined as:

ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
HRESULT Status;

If CallbackType==ThreadCallback, then it is:

ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
MINIDUMP_THREAD_CALLBACK Thread;

If CallbackType==ThreadExCallback, then it is:

ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
MINIDUMP_THREAD_EX_CALLBACK ThreadEx;

And so on (this is from MSDN) -- it looks like that 4th member can be one of 8 different types, depending on what CallbackType is equal to. Internally, Windows will use the same chunk of memory for all of these structures (padding the smaller ones to the largest one's size). In C++, it's an easy typecast to get the type you need.

I'm not sure how to do this in C#. I have used MiniDumpWriteDump in C++, but never used the callback function. If you could be sure that CallbackType was always one value, you could just code that one structure, but I don't know if this is the case.

Sorry I can't provide more information, but maybe this helps describe the problem.

Marc Bernier
Yes, i am having troubles defining the union part. Unfortunately, i cannot define the structure just for one particular callbacktype, since all of them will be called. Thanks anyway
anchandra
The only way to port in C# a C structure with union is to use the StructLayoutAttribute with LayoutKind.Explicit option and to explicitely write the offset of each field with FieldOffsetAttribute. I'll put an example in another answer.
cedrou
A: 

Use this code to define a structure with union in 32 bits:

[StructLayout(LayoutKind.Explicit)]
internal struct MINIDUMP_CALLBACK_INPUT 
{
  [FieldOffset(0)]
  UInt32 ProcessId;

  [FieldOffset(4)]
  IntPtr ProcessHandle;

  [FieldOffset(8)]
  UInt32 CallbackType;

  [FieldOffset(12)]
  MINIDUMP_THREAD_CALLBACK Thread;
  [FieldOffset(12)]
  MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
  [FieldOffset(12)]
  MINIDUMP_MODULE_CALLBACK Module;
  [FieldOffset(12)]
  MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
  [FieldOffset(12)]
  MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
};

I think that on a 64 bits platform, the offsets should be respectively { 0, 8, 16, 24 } or { 0, 4, 12, 16 } whether ULONG is 64 or 32 bits.


Edit

I think you can use the MarshalAsAttribute to specifically convert fields:

  [StructLayout(LayoutKind.Sequential)]
  struct VS_FIXEDFILEINFO
  {
    UInt32 dwSignature;
    UInt32 dwStrucVersion;
    UInt32 dwFileVersionMS;
    UInt32 dwFileVersionLS;
    UInt32 dwProductVersionMS;
    UInt32 dwProductVersionLS;
    UInt32 dwFileFlagsMask;
    UInt32 dwFileFlags;
    UInt32 dwFileOS;
    UInt32 dwFileType;
    UInt32 dwFileSubtype;
    UInt32 dwFileDateMS;
    UInt32 dwFileDateLS;
  }

  [StructLayout (LayoutKind.Sequential)]
  struct MINIDUMP_MODULE_CALLBACK
  {
    [MarshalAs(UnmanagedType.LPWStr)]
    String                      FullPath;
    UInt64                      BaseOfImage;
    UInt32                      SizeOfImage;
    UInt32                      CheckSum;
    UInt32                      TimeDateStamp;
    VS_FIXEDFILEINFO            VersionInfo;
    IntPtr                      CvRecord;
    UInt32                      SizeOfCvRecord;
    IntPtr                      MiscRecord;
    UInt32                      SizeOfMiscRecord;
  }
cedrou
ULONG is 32 bits on Win64 too.
Mattias S
It is not as easy as that, since MINIDUMP_MODULE_CALLBACK is a structure itself, that contains a PWCHAR. How do you define that?
anchandra