2009-09-01 10 views
5

Desidero utilizzare la funzione MiniDumpWriteDump per creare alcuni file di dump personalizzati (principalmente, voglio esportare un file di dump che contiene la quantità minima di informazioni per i thread callstacks), ma ho difficoltà a definire il file strutture che hanno bisogno di essere passato come parametro alla funzione di callbackChiama MiniDumpWriteDump con callback

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

E la chiamata seguente aspetto:

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

Quindi la mia domanda è come definire MINIDUMP_CALLBACK_INPUT:

La definizione delle strutture in C che pongono problemi sono:

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 

risposta

2

Non è una risposta diretta della tua domanda, ma una soluzione alternativa ...

Non si conosce il ClrDump lib, che fa quello che si bisogno ? L'ho usato per un progetto diversi anni fa e per me va bene.


risposta all'autore commento:

Leggi sul sito:

ClrDump può produrre piccole minidumps che contengono informazioni sufficienti per recuperare gli stack di chiamate di tutti i thread del applicazione.

ho avvolto l'API nella seguente classe:

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

e poi, qualche parte nel mio codice di inizializzazione, ho chiamato il metodo RegisterFilter che registra un filtro interno per le eccezioni non gestite nel processo corrente. Se il processo si blocca con un'eccezione non gestita (può essere un'eccezione nativa o gestita), il filtro la cattura e crea un minidump (con il nome file specificato). Ecco un codice di esempio per questo:

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

Si può leggere this article per comprendere le diverse opzioni MINIDUMP_TYPE, ma penso che quella di base (MiniDumpNormal) potrebbe soddisfare le vostre esigenze.

+0

Sì, ho letto un po 'di clrdump lib, ma non mi è chiaro come posso ottenere quello che voglio con esso. Come posso ottenere la quantità minima di informazioni per i callstack di thread? Usando i filtri? Potresti darmi un esempio di codice? – anchandra

0

Immagino che sia l'unione che ti dà problemi?

Se CallbackType == KernelMinidumpStatusCallback, allora la struttura CALLBACK_INPUT è definita come:

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

Se CallbackType == ThreadCallback, allora è:

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

Se CallbackType == ThreadExCallback, allora è:

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

E così via (questo è da MSDN): sembra che il 4 ° membro possa essere uno di 8 tipi diversi, a seconda di come CallbackType è uguale a.Internamente, Windows utilizzerà lo stesso blocco di memoria per tutte queste strutture (sostituendo le più piccole alla dimensione maggiore). In C++, è un semplice typecast per ottenere il tipo che ti serve.

io non sono sicuro di come fare questo in C#. Ho usato MiniDumpWriteDump in C++, ma non ho mai usato la funzione di callback. Se potessi essere sicuro che CallbackType fosse sempre un valore, potresti semplicemente codificare quella struttura, ma non so se questo è il caso.

Spiacente, non posso fornire ulteriori informazioni, ma forse questo aiuta a descrivere il problema.

+0

Sì, sto avendo problemi a definire la parte sindacale. Sfortunatamente, non posso definire la struttura solo per un particolare callbacktype, dal momento che tutti saranno chiamati. Grazie comunque – anchandra

+1

L'unico modo per porto in C# una struttura C con l'unione è quello di utilizzare l'StructLayoutAttribute con l'opzione LayoutKind.Explicit e di scrivere esplicitamente l'offset di ogni campo con FieldOffsetAttribute. Metterò un esempio in un'altra risposta. – cedrou

0

Usa questo codice per definire una struttura con un'unione in 32 bit:

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

ritengo che su una piattaforma 64 bit, gli offset devono essere rispettivamente {0, 8, 16, 24} o {0 , 4, 12, 16} se ULONG è 64 o 32 bit.


Modifica

Penso che si possa utilizzare il MarshalAsAttribute per convertire specificamente campi:

[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

Anche ULONG ha 32 bit su Win64. –

+0

Non è così semplice, dato che MINIDUMP_MODULE_CALLBACK è una struttura stessa, che contiene un PWCHAR. Come lo definisci? – anchandra

Problemi correlati