Вызов MiniDumpWriteDump с обратным вызовом - PullRequest
5 голосов
/ 01 сентября 2009

Я хочу использовать функцию MiniDumpWriteDump для создания некоторых пользовательских файлов дампа (в основном я хочу экспортировать файл дампа, который содержит минимальное количество информации для стеков вызовов потока), но у меня возникают трудности при определении структур, которые необходимо передаваться в качестве параметра в функцию обратного вызова

[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);

И звонок выглядит так:

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);

Так что мой вопрос в том, как определить MINIDUMP_CALLBACK_INPUT:

Определение структур в C, которые создают проблемы:

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

Ответы [ 3 ]

2 голосов
/ 01 сентября 2009

Это не прямой ответ на ваш вопрос, а обходной путь ...

Знаете ли вы ClrDump lib, который делает то, что вам нужно? Я использовал его для проекта несколько лет назад, и он прекрасно работает для меня.


Ответ на комментарий автора :

Читайте на сайте:

ClrDump может создавать небольшие мини-дампы, которые содержат достаточно информации для восстановления стеков вызовов всех потоков в приложении.

Я обернул API в следующем классе:

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
  }
}

и затем где-то в своем коде инициализации я вызвал метод RegisterFilter, который регистрирует внутренний фильтр для необработанных исключений в текущем процессе. Если происходит сбой процесса с необработанным исключением (это может быть собственное или управляемое исключение), фильтр ловит его и создает мини-дамп (с указанным именем файла). Вот пример кода для этого:

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);

Вы можете прочитать эту статью , чтобы понять различные параметры MINIDUMP_TYPE, но я думаю, что базовый (MiniDumpNormal) может удовлетворить ваши потребности.

1 голос
/ 02 сентября 2009

Используйте этот код для определения структуры с объединением в 32 бита:

[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;
};

Я думаю, что на 64-битной платформе смещения должны быть соответственно {0, 8, 16, 24} или {0, 4, 12, 16} независимо от того, является ли ULONG 64 или 32-битным.


Редактировать

Я думаю, вы можете использовать атрибут MarshalAsAttribute для специального преобразования полей:

  [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;
  }
0 голосов
/ 01 сентября 2009

Я так понимаю, что это союз, который доставляет вам неприятности?

Если CallbackType == KernelMinidumpStatusCallback, то структура CALLBACK_INPUT определяется как:

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

Если CallbackType == ThreadCallback, то это:

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

Если CallbackType == ThreadExCallback, то это:

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

И так далее (это от MSDN ) - похоже, что 4-й член может быть одного из 8 различных типов, в зависимости от того, чему равен CallbackType. Внутренне, Windows будет использовать один и тот же кусок памяти для всех этих структур (добавляя меньшие к наибольшему размеру). В C ++ легко можно получить нужный тип.

Я не уверен, как это сделать в C #. Я использовал MiniDumpWriteDump в C ++, но никогда не использовал функцию обратного вызова. Если бы вы могли быть уверены, что CallbackType всегда был одним значением, вы могли бы просто закодировать эту одну структуру, но я не знаю, так ли это.

Извините, я не могу предоставить больше информации, но, возможно, это поможет описать проблему.

...