Почему генерация мини-дампов внутри UnobservedTaskException вызывает AccessViolationException

1

У меня есть процесс, который создает мини-дампы, когда происходят необработанные исключения (с использованием как AppDomain.CurrentDomain.UnhandledException и TaskScheduler.UnobservedTaskException). Дамп отлично работает с обработчиком необработанных исключений, но создает AccessViolationException при вызове из ненаблюдаемого обработчика исключения задачи (см. Пример кода ниже).

Из того, что я читал в MSDN и StackOverflow, ключевое различие между двумя путями кода состоит в том, что последнее происходит из потока финализатора, и я предполагаю, что состояние потока или, возможно, безопасность, препятствуют этой операции.

Есть ли у кого-нибудь какая-либо информация или ссылки, которые объясняют, почему операция мини-дампа терпит неудачу в незаметном случае исключения исключений? Я рад принять, что то, что я пытаюсь просто, невозможно, но я действительно хотел бы подтвердить это и узнать, почему...

В приведенном ниже коде демонстрируется моя проблема, вызывая необработанное исключение внутри задачи и заставляя сборку мусора запускать исключение незаметного потока. Затем вызов MiniDumpWriteDump генерирует AccessViolationException.

using System;
using System.IO;
using System.Runtime.ConstrainedExecution;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
using System.Threading.Tasks;

namespace ExceptionTest
{
    public class ClassThatThrows
    {
        public void Throw()
        {
            var t = new Task(() =>
            {
                Console.WriteLine("Throwing...");
                throw new NullReferenceException();
            });

            t.Start();
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            TaskScheduler.UnobservedTaskException += HandleUnobservedTaskException;

            var test = new ClassThatThrows();
            test.Throw();

            Thread.Sleep(1000);

            GC.Collect();
            GC.WaitForPendingFinalizers();

            Console.ReadLine();
        }

        [HandleProcessCorruptedStateExceptions]
        [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
        [SecurityCritical]
        private static void HandleUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            WriteMiniDump();
        }

        private static void WriteMiniDump()
        {
            var miniDumpFilePath = GetMiniDumpFilePath();

            using (var fileStream = new FileStream(miniDumpFilePath, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                MiniDumpExceptionInformation exceptionInfo;
                exceptionInfo.ThreadId = GetCurrentWin32ThreadId();
                exceptionInfo.ClientPointers = false;
                exceptionInfo.ExceptionPointers = Marshal.GetExceptionPointers();

                var currentProcess = GetCurrentProcess();
                var currentProcessId = GetCurrentProcessId();

                var safeFileHandle = fileStream.SafeFileHandle;
                MiniDumpWriteDump(currentProcess, currentProcessId, safeFileHandle.DangerousGetHandle(), 0x00000000, ref exceptionInfo, IntPtr.Zero, IntPtr.Zero);
            }
        }

        protected static string GetMiniDumpFilePath()
        {
            var timestamp = DateTime.Now.ToString("yyyyMMddTHHmmss");
            var fileName = string.Format("MiniDump.{0}.mdmp", timestamp);
            return Path.Combine(Path.GetTempPath(), fileName);
        }

        [DllImport("Kernel32", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
        public static extern uint GetCurrentWin32ThreadId();

        [DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
        public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, IntPtr hFile, uint dumpType, ref MiniDumpExceptionInformation expParam, IntPtr userStreamParam, IntPtr callbackParam);

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", ExactSpelling = true)]
        public static extern IntPtr GetCurrentProcess();

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcessId", ExactSpelling = true)]
        public static extern uint GetCurrentProcessId();

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct MiniDumpExceptionInformation
        {
            public uint ThreadId;
            public IntPtr ExceptionPointers;
            [MarshalAs(UnmanagedType.Bool)]
            public bool ClientPointers;
        }
    }
}

благодаря

  • 0
    Вы пытались IntPtr проходящий IntPtr ?
  • 0
    Нет репро, нет явных ошибок, которые я вижу. Версии Windows, dbghelp.dll и .NET должны быть задокументированы в этом вопросе. Все, что я могу порекомендовать, это добавить GC.KeepAlive(e.Exception); к обработчику событий, однако, выстрел с большого расстояния. Ищите экологические проблемы. А также включите неуправляемую отладку, сервер Microsoft Symbol и опубликуйте трассировку стека, которую вы видите, когда он бомбит.
Теги:
multithreading
exception-handling
c#-4.0
task-parallel-library

1 ответ

0
Лучший ответ

Я попытался выполнить предложение Yuval с моей домашней машины и не смог воспроизвести проблему, которая сразу заставила меня рассмотреть версию dbghelp.dll на исходной машине на работе.

После размещения dbghelp.dll из последней версии Windows 8.1 SDK в моей исполняемой папке проблема исчезла, поэтому я могу указать палку вины на устаревшую dbghelp.dll, а не некоторую неясную проблему с С# с созданием мини-дампов из финализатора нить.

Для справки, dbghelp.dll 6.1.7601.17514 демонстрирует проблему, и dbghelp.dll 6.3.9600.17029 подтверждено для устранения проблемы. Стек вызова из исключения (пока не отображается много) показан ниже:

    ntdll.dll!NtWaitForSingleObject()
KernelBase.dll!WaitForSingleObjectEx()
msvcr110_clr0400.dll!__C_specific_handler()
ntdll.dll!RtlpExecuteHandlerForException()
ntdll.dll!RtlDispatchException()
ntdll.dll!KiUserExceptionDispatch()
dbghelp.dll!MiniDumpWriteDump()
[Managed to Native Transition]  
    ExceptionTest.exe!ExceptionTest.Program.WriteMiniDump() Line 66 C#
ExceptionTest.exe!ExceptionTest.Program.HandleUnobservedTaskException(object sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs e) Line 48 C#
mscorlib.dll!System.Threading.Tasks.TaskScheduler.PublishUnobservedTaskException(object sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs ueea)
mscorlib.dll!System.Threading.Tasks.TaskExceptionHolder.Finalize()

Ещё вопросы

Сообщество Overcoder
Наверх
Меню