2009-09-01 14 views
5

Quiero usar la función MiniDumpWriteDump para crear algunos archivos de volcado personalizados (principalmente, quiero exportar un archivo de volcado que contiene la cantidad mínima de información para las listas de llamadas de subprocesos), pero tengo dificultades para definir el estructuras que deben ser pasados ​​como parámetro a la función de devolución de llamadaLlamar a MiniDumpWriteDump con devolución de llamada

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

Y la llamada se ve así:

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

Así que mi pregunta es cómo definir MINIDUMP_CALLBACK_INPUT:

La definición de las estructuras en C que plantean problemas son:

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 

Respuesta

2

No es una respuesta directa de su pregunta, sino una solución ...

¿Conoce la lib ClrDump, que hace lo que usted necesitar ? Lo he usado para un proyecto hace varios años y funciona bien para mí.


respuesta a los comentarios del autor:

leer en el sitio:

ClrDump puede producir pequeñas minivolcados que contienen suficiente información para recuperar las pilas de llamadas de todos los hilos en el solicitud.

que envuelve la API en la siguiente clase:

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

y luego, en algún lugar de mi código de inicialización, me llamaron la RegisterFilter método que registra un filtro interno para las excepciones no controladas en el proceso actual. Si el proceso se bloquea con una excepción no controlada (puede ser una excepción nativa o administrada), el filtro lo captura y crea un minivolcado (con el nombre de archivo especificado). Aquí es un código de ejemplo para esto:

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

Usted puede leer this article para entender las diferentes opciones MINIDUMP_TYPE, pero creo que el básico (MiniDumpNormal) podría adaptarse a sus necesidades.

+0

Sí, he leído un poco sobre lib clrdump, pero no me queda claro cómo puedo obtener lo que quiero con eso. ¿Cómo puedo obtener la cantidad mínima de información para los callstacks de hilo? ¿Usando filtros? ¿Me podría dar una muestra de código? – anchandra

0

Supongo que es la unión la que le está causando problemas?

Si, a continuación, la estructura CALLBACK_INPUT se define CallbackType == KernelMinidumpStatusCallback como:

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

Si CallbackType == ThreadCallback, entonces es:

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

Si CallbackType == ThreadExCallback, entonces es:

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

Y así sucesivamente (esto es de MSDN): parece que el 4 ° miembro puede ser uno de los 8 tipos diferentes, en función de lo que CallbackType sea igual.Internamente, Windows usará el mismo bloque de memoria para todas estas estructuras (agregando las más pequeñas al tamaño más grande). En C++, es un tipo sencillo para obtener el tipo que necesita.

No estoy seguro de cómo hacer esto en C#. He usado MiniDumpWriteDump en C++, pero nunca he usado la función de devolución de llamada. Si pudiera estar seguro de que CallbackType tiene siempre un valor, podría simplemente codificar esa estructura, pero no sé si este es el caso.

Lo siento, no puedo brindar más información, pero quizás esto ayude a describir el problema.

+0

Sí, estoy teniendo problemas para definir la parte de la unión. Desafortunadamente, no puedo definir la estructura solo para un tipo de devolución de llamada en particular, ya que se llamarán a todos ellos. Gracias de todos modos – anchandra

+1

La única forma de portar en C# una estructura C con unión es usar StructLayoutAttribute con la opción LayoutKind.Explicit y escribir explícitamente el desplazamiento de cada campo con FieldOffsetAttribute. Pondré un ejemplo en otra respuesta. – cedrou

0

Usa este código para definir una estructura con la unión en 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; 
}; 

creo que en una plataforma de 64 bits, las compensaciones deberían ser, respectivamente, {0, 8, 16, 24} o {0 , 4, 12, 16} si ULONG es 64 o 32 bits.


Editar

Creo que se puede utilizar el MarshalAsAttribute para convertir específicamente campos:

[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

ULONG es de 32 bits en Win64 también. –

+0

No es tan fácil como eso, ya que MINIDUMP_MODULE_CALLBACK es una estructura en sí misma, que contiene un PWCHAR. ¿Cómo defines eso? – anchandra

Cuestiones relacionadas