Показаны сообщения с ярлыком Minidump. Показать все сообщения
Показаны сообщения с ярлыком Minidump. Показать все сообщения

17 мая 2017 г.

MiniDumpWriteDump. Создаём minidump на C#.

Что такое minidump?

Minidump - это, в зависимости от запроса, полный или частичный "слепок" оперативной памяти работающего процесса с сохранением полного или частичного состояния на момент получения снимка.
Как это можно поможет в работе? Дело в том, что minidump можно создавать самому, а начиная с Framework 4.0 - Visual Studio при открытии minidump файла может показать точку останова, стек вызова и даже локальные процессы. Если конечно их не убрал /Release оптимизатор. Иными словами мы увидим такую же картину, как если бы мы работающее приложение поставили на паузу и решили посмотреть стек.
Заинтересовало не так ли? Тогда приступим!

Способы создания.

Известные мне способы создания дампа:
  1. Создание дампа внутри вашего приложения вызывая метод MiniDumpWriteDump из dbghelp.dll.
  2. Используя какие то внешние (out of process) утилиты:
    Стандартный диспетчер задач, Process Explorer или Process Hacker и т.д

Создание Minidump внутри приложения


Для создания дампа внутри нашего приложения нам необходимо всего лишь вызвать функцию MiniDumpWriteDump из библиотеки dbghelp.dll, которая есть у всех, или почти всех, версий Windows. Из отличий версий могут быть только флаги параметра, которые добавлялись в ходе развития библиотеки.
Зачем создавать дамп из тела приложения? Создание таким образом дает возможность контролировать место останова, для дальнейшего анализа дампа. Дампы созданные из диспетчера задач не могу гарантировать, что при открытии в студию вы попадете в "полезное" место и в котором будет все, что требуется для решении вашей задачи или проблемы.

Я создал простенькое консольное приложение для демонстрации работы:
  1. Код  метода Main():
    namespace Dumper
    {
        using System;
        using System.IO;
        using System.Linq;
        using System.Runtime.CompilerServices;
        using System.Threading;
        using Dumps;
        
        internal static class Program
        {
            private static bool _stopThread;
     
            /// <summary>
            /// Entry point.
            /// </summary>
            static void Main(string[] args)
            {
                Directory.GetFiles(DumpUtils.DumpDirectory).ToList().ForEach(File.Delete);
                CreateThread();
                TestMethod();
                _stopThread = true;
            }
     
            private static void CreateThread()
            {
                new Thread(() =>
                {
                    while (!_stopThread)
                    {
                        Console.WriteLine("while(true)");
                    }
                })
                {
                    Name = "While(true) thread"
                }.Start();
            }
     
            [MethodImpl(MethodImplOptions.NoOptimization)]
            private static void TestMethod()
            {
                var dateTime = DateTime.Now;
                try
                {
                    Console.WriteLine(dateTime);
     
                    int someDouble = 0;
                    someDouble = 10 / someDouble;
                }
                catch (Exception)
                {
                    DumpUtils.WriteDump();
                }
            }
        }
    }
    
  2. Код класса создающий Minidump файл:
    namespace Dumper.Dumps
    {
        using System;
        using System.Diagnostics;
        using System.IO;
        using System.Reflection;
        using System.Runtime.CompilerServices;
        using System.Runtime.InteropServices;
     
        /// <summary>
        /// Minidump support tools.
        /// </summary>
        public static class DumpUtils
        {
            /// <summary>
            /// Folder for saved minidumps.
            /// </summary>
            public const string DumpDirectory = "Minidump";
     
            [DllImport("dbghelp.dll")]
            private static extern bool MiniDumpWriteDump(IntPtr hProcess, int processId, IntPtr hFile, int dumpType,
                IntPtr exceptionParam, IntPtr userStreamParam, IntPtr callStackParam);
     
            /// <summary>
            /// Write minidump to file.
            /// </summary>
            /// <param name="minidumpType">Minidump flag(s).</param>
            [MethodImpl(MethodImplOptions.NoOptimization)]
            public static bool WriteDump(
                MinidumpType minidumpType = MinidumpType.MiniDumpWithFullMemory |
                MinidumpType.MiniDumpWithHandleData |
                MinidumpType.MiniDumpWithUnloadedModules |
                MinidumpType.MiniDumpWithFullMemoryInfo |
                MinidumpType.MiniDumpWithThreadInfo)
            {
                try
                {
                    if (!Directory.Exists(DumpDirectory))
                    {
                        Directory.CreateDirectory(DumpDirectory);
                    }
     
                    var currentProcess = Process.GetCurrentProcess();
                    var fileName = GetNewDumpFileName(currentProcess.ProcessName);
                    var currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                    var filePath = Path.Combine(currentDir, DumpDirectory, fileName);
                    var handler = currentProcess.Handle;
                    var processId = currentProcess.Id;
     
                    using (var fileStream = new FileStream(filePath, FileMode.CreateNew))
                    {
                        return MiniDumpWriteDump(
                            handler,
                            processId,
                            fileStream.SafeFileHandle.DangerousGetHandle(),
                            (int) minidumpType,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            IntPtr.Zero);
                    }
                }
                catch (Exception)
                {
                    return false;
                }
            }
     
            private static string GetNewDumpFileName(string processName)
            {
                return string.Format("{0}_{1}_{2}.dmp", processName,
                    DateTime.Now.ToString("yyyy-dd-mm_HH-mm-ss"),
                    Path.GetRandomFileName().Replace("."""));
            }
        }
    }