#c# #memory-dump
#c# #дамп памяти
Вопрос:
У меня есть System.Diagnostics.Process
объект. Моя программа на C # отслеживает это на предмет некоторого состояния. При выполнении условия я хочу получить полный дамп памяти процесса.
Каков наилучший способ добиться этого?
Есть ли способ, который предоставляется CLR?
Комментарии:
1. Полный дамп памяти? То есть «всего адресного пространства»?
Ответ №1:
Вы могли бы использовать ProcDump из Sysinternals и заставить свою программу C # вызывать его при необходимости.
Process.Start("procdump " otherProgramPID.ToString());
Ответ №2:
В Microsoft MSDN приведен пример кода, показывающий, как добиться этого с помощью кода C # в вашем собственном приложении:
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
// !load \ddelementaryautowatsonvsdbgvsdbg.dll
namespace CreateMiniDump
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded = (ol, el) =>
{
IntPtr hFile = IntPtr.Zero;
try
{
if (IntPtr.Size == 4)
{
this.Title = "CreateMiniDump Running as 32 bit, creating 32 bit dumps";
}
else
{
this.Title = "CreateMiniDump Running as 64 bit, creating 64 bit dumps";
}
this.Height = 800;
this.Width = 800;
var sp = new StackPanel() { Orientation = Orientation.Vertical };
this.Content = sp;
var dumpFileName = System.IO.Path.Combine(
System.IO.Path.GetTempPath(),
"testdump.dmp");
var txtDumpFile = new TextBox()
{
Text = dumpFileName
};
sp.Children.Add(txtDumpFile);
var txtProcName = new TextBox()
{
ToolTip = "Process name without extension",
Text = "devenv"
};
sp.Children.Add(txtProcName);
var lstDumpType = new ListBox()
{
// allow multi select
SelectionMode = SelectionMode.Extended
};
lstDumpType.ItemsSource = Enum.GetValues(typeof(NativeMethods._MINIDUMP_TYPE));
// set initial value
// for a dump with memory info we want these:
foreach (var val in new[] {
NativeMethods._MINIDUMP_TYPE.MiniDumpWithFullMemory,
NativeMethods._MINIDUMP_TYPE.MiniDumpWithFullMemoryInfo,
NativeMethods._MINIDUMP_TYPE.MiniDumpWithHandleData,
NativeMethods._MINIDUMP_TYPE.MiniDumpWithThreadInfo
})
{
var nval = (int)(Math.Log((int)val) / Math.Log(2)) 1;
lstDumpType.SelectedItems.Add(
lstDumpType.Items[nval]
);
}
sp.Children.Add(lstDumpType);
var btnGo = new Button()
{
Content = "_Create Dump",
Width = 200
};
sp.Children.Add(btnGo);
var txtStatus = new TextBox()
{
IsUndoEnabled = false,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
MaxHeight = 400
};
sp.Children.Add(txtStatus);
btnGo.Click = (ob, eb) =>
{
try
{
var sw = new Stopwatch();
sw.Start();
dumpFileName = txtDumpFile.Text.Trim();
if (System.IO.File.Exists(dumpFileName))
{
System.IO.File.Delete(dumpFileName);
}
hFile = NativeMethods.CreateFile(
dumpFileName,
NativeMethods.EFileAccess.GenericWrite,
NativeMethods.EFileShare.None,
lpSecurityAttributes: IntPtr.Zero,
dwCreationDisposition: NativeMethods.ECreationDisposition.CreateAlways,
dwFlagsAndAttributes: NativeMethods.EFileAttributes.Normal,
hTemplateFile: IntPtr.Zero
);
if (hFile == NativeMethods.INVALID_HANDLE_VALUE)
{
var hr = Marshal.GetHRForLastWin32Error();
var ex = Marshal.GetExceptionForHR(hr);
throw ex;
}
NativeMethods._MINIDUMP_TYPE dumpType = NativeMethods._MINIDUMP_TYPE.MiniDumpNormal; // 0
foreach (var item in lstDumpType.SelectedItems)
{
var dt = (NativeMethods._MINIDUMP_TYPE)item;
dumpType |= dt;
}
var exceptInfo = new NativeMethods.MINIDUMP_EXCEPTION_INFORMATION();
var proc = Process.GetProcessesByName(
txtProcName.Text.Trim()
).FirstOrDefault();
if (!proc.Is32BitProcess() amp;amp; IntPtr.Size == 4)
{
throw new InvalidOperationException(
"Can't create 32 bit dump of 64 bit process"
);
}
var result = NativeMethods.MiniDumpWriteDump(
proc.Handle,
proc.Id,
hFile,
dumpType,
ref exceptInfo,
UserStreamParam: IntPtr.Zero,
CallbackParam: IntPtr.Zero
);
if (result == false)
{
var hr = Marshal.GetHRForLastWin32Error();
var ex = Marshal.GetExceptionForHR(hr);
throw ex;
}
txtStatus.Text = string.Format(
"Dump Created. Pid= {0} {1}r File size = {2:n0}rElapsed = {3:n3}r",
proc.Id,
dumpType,
(new System.IO.FileInfo(dumpFileName)).Length,
sw.Elapsed.TotalSeconds
);
}
catch (Exception ex)
{
txtStatus.Text = ex.ToString();
}
finally
{
NativeMethods.CloseHandle(hFile);
}
};
}
catch (Exception ex)
{
this.Content = ex.ToString();
}
};
}
}
public static class ExtensionMethods
{
public static bool Is32BitProcess(this Process proc)
{
bool fIs32bit = false;
// if we're runing on 32bit, default to true
if (IntPtr.Size == 4)
{
fIs32bit = true;
}
bool fIsRunningUnderWow64 = false;
// if machine is 32 bit then all procs are 32 bit
if (NativeMethods.IsWow64Process(NativeMethods.GetCurrentProcess(), out fIsRunningUnderWow64)
amp;amp; fIsRunningUnderWow64)
{
// current OS is 64 bit
if (NativeMethods.IsWow64Process(proc.Handle, out fIsRunningUnderWow64)
amp;amp; fIsRunningUnderWow64)
{
fIs32bit = true;
}
else
{
fIs32bit = false;
}
}
return fIs32bit;
}
}
public static partial class NativeMethods
{
[DllImport("Dbghelp.dll")]
public static extern bool MiniDumpWriteDump(
IntPtr hProcess,
int ProcessId,
IntPtr hFile,
_MINIDUMP_TYPE DumpType,
ref MINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
IntPtr UserStreamParam,
IntPtr CallbackParam
);
//https://msdn.microsoft.com/en-us/library/windows/desktop/ms680519(v=vs.85).aspx?f=255amp;MSPPError=-2147217396
[Flags]
public enum _MINIDUMP_TYPE
{
MiniDumpNormal = 0x00000000,
MiniDumpWithDataSegs = 0x00000001,
MiniDumpWithFullMemory = 0x00000002,
MiniDumpWithHandleData = 0x00000004,
MiniDumpFilterMemory = 0x00000008,
MiniDumpScanMemory = 0x00000010,
MiniDumpWithUnloadedModules = 0x00000020,
MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
MiniDumpFilterModulePaths = 0x00000080,
MiniDumpWithProcessThreadData = 0x00000100,
MiniDumpWithPrivateReadWriteMemory = 0x00000200,
MiniDumpWithoutOptionalData = 0x00000400,
MiniDumpWithFullMemoryInfo = 0x00000800,
MiniDumpWithThreadInfo = 0x00001000,
MiniDumpWithCodeSegs = 0x00002000,
MiniDumpWithoutAuxiliaryState = 0x00004000,
MiniDumpWithFullAuxiliaryState = 0x00008000,
MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
MiniDumpIgnoreInaccessibleMemory = 0x00020000,
MiniDumpWithTokenInformation = 0x00040000,
MiniDumpWithModuleHeaders = 0x00080000,
MiniDumpFilterTriage = 0x00100000,
MiniDumpValidTypeFlags = 0x001fffff,
};
/*
*
https://msdn.microsoft.com/en-us/library/windows/desktop/bb513622(v=vs.85).aspx
WER_DUMP_TYPE:
WerDumpTypeHeapDump
MiniDumpWithDataSegs
MiniDumpWithProcessThreadData
MiniDumpWithHandleData
MiniDumpWithPrivateReadWriteMemory
MiniDumpWithUnloadedModules
MiniDumpWithFullMemoryInfo
MiniDumpWithThreadInfo (Windows 7 and later)
MiniDumpWithTokenInformation (Windows 7 and later)
MiniDumpWithPrivateWriteCopyMemory
WerDumpTypeMicroDump
MiniDumpWithDataSegs
MiniDumpWithUnloadedModules
MiniDumpWithProcessThreadData
MiniDumpWithoutOptionalData
WerDumpTypeMiniDump
MiniDumpWithDataSegs
MiniDumpWithUnloadedModules
MiniDumpWithProcessThreadData
MiniDumpWithTokenInformation
*/
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct MINIDUMP_EXCEPTION_INFORMATION
{
public uint ThreadId;
public IntPtr ExceptionPointers;
public int ClientPointers;
}
[DllImport("kernel32.dll",
SetLastError = true,
CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll",
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
public static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
[Flags]
public enum EFileAccess : uint
{
//
// Standart Section
//
AccessSystemSecurity = 0x1000000, // AccessSystemAcl access type
MaximumAllowed = 0x2000000, // MaximumAllowed access type
Delete = 0x10000,
ReadControl = 0x20000,
WriteDAC = 0x40000,
WriteOwner = 0x80000,
Synchronize = 0x100000,
StandardRightsRequired = 0xF0000,
StandardRightsRead = ReadControl,
StandardRightsWrite = ReadControl,
StandardRightsExecute = ReadControl,
StandardRightsAll = 0x1F0000,
SpecificRightsAll = 0xFFFF,
FILE_READ_DATA = 0x0001, // file amp; pipe
FILE_LIST_DIRECTORY = 0x0001, // directory
FILE_WRITE_DATA = 0x0002, // file amp; pipe
FILE_ADD_FILE = 0x0002, // directory
FILE_APPEND_DATA = 0x0004, // file
FILE_ADD_SUBDIRECTORY = 0x0004, // directory
FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
FILE_READ_EA = 0x0008, // file amp; directory
FILE_WRITE_EA = 0x0010, // file amp; directory
FILE_EXECUTE = 0x0020, // file
FILE_TRAVERSE = 0x0020, // directory
FILE_DELETE_CHILD = 0x0040, // directory
FILE_READ_ATTRIBUTES = 0x0080, // all
FILE_WRITE_ATTRIBUTES = 0x0100, // all
//
// Generic Section
//
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
SPECIFIC_RIGHTS_ALL = 0x00FFFF,
FILE_ALL_ACCESS =
StandardRightsRequired |
Synchronize |
0x1FF,
FILE_GENERIC_READ =
StandardRightsRead |
FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
Synchronize,
FILE_GENERIC_WRITE =
StandardRightsWrite |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA |
FILE_APPEND_DATA |
Synchronize,
FILE_GENERIC_EXECUTE =
StandardRightsExecute |
FILE_READ_ATTRIBUTES |
FILE_EXECUTE |
Synchronize
}
[Flags]
public enum EFileShare : uint
{
/// <summary>
///
/// </summary>
None = 0x00000000,
/// <summary>
/// Enables subsequent open operations on an object to request read access.
/// Otherwise, other processes cannot open the object if they request read access.
/// If this flag is not specified, but the object has been opened for read access, the function fails.
/// </summary>
Read = 0x00000001,
/// <summary>
/// Enables subsequent open operations on an object to request write access.
/// Otherwise, other processes cannot open the object if they request write access.
/// If this flag is not specified, but the object has been opened for write access, the function fails.
/// </summary>
Write = 0x00000002,
/// <summary>
/// Enables subsequent open operations on an object to request delete access.
/// Otherwise, other processes cannot open the object if they request delete access.
/// If this flag is not specified, but the object has been opened for delete access, the function fails.
/// </summary>
Delete = 0x00000004
}
public enum ECreationDisposition : uint
{
/// <summary>
/// Creates a new file. The function fails if a specified file exists.
/// </summary>
New = 1,
/// <summary>
/// Creates a new file, always.
/// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes,
/// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
/// </summary>
CreateAlways = 2,
/// <summary>
/// Opens a file. The function fails if the file does not exist.
/// </summary>
OpenExisting = 3,
/// <summary>
/// Opens a file, always.
/// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
/// </summary>
OpenAlways = 4,
/// <summary>
/// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
/// The calling process must open the file with the GENERIC_WRITE access right.
/// </summary>
TruncateExisting = 5
}
[Flags]
public enum EFileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWow64Process(
[In] IntPtr hProcess,
[Out, MarshalAs(UnmanagedType.Bool)] out bool wow64Process
);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetCurrentProcess();
}
}
Комментарии:
1. Я обнаружил, что лучше установить ExceptionParam в IntPtr.Zero, чтобы избежать проблем, когда один процесс сбрасывает память другого
2. Нет ли меньшего решения? Я помню, что видел это для C , и там было около пяти строк?
3. @Dominique наверняка есть другие решения. Я предоставил ответ, цитируемый из MSDN. Не стесняйтесь публиковать свой собственный ответ :). Пользователь «Zain Rizvi» опубликовал более короткий ответ для C .
Ответ №3:
Вы можете попробовать вызвать метод C из своего кода на C #. Вот пример того, как вы могли бы это объявить:
[DllImport("dbghelp.dll",
EntryPoint = "MiniDumpWriteDump",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode,
ExactSpelling = true,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool MiniDumpWriteDump(
IntPtr hProcess,
uint processId,
SafeHandle hFile,
MINIDUMP_TYPE dumpType,
IntPtr expParam,
IntPtr userStreamParam,
IntPtr callbackParam);
Вы можете ознакомиться с кодом этого проекта для примера:
https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Infrastructure/MiniDumpNativeMethods.cs
https://github.com/projectkudu/kudu/blob/2db563be679bb60656050ec3f04945086f07b360/Kudu.Core/Infrastructure/ProcessExtensions.cs