Как пройти цепочку попыток / уловов .NET, чтобы решить сгенерировать минидамп

Мы столкнулись с трудностями при смешивании Задач с помощью нашего обработчика сбоев верхнего уровня и пытаемся найти обходной путь. Я надеюсь, что у кого-то есть идеи .

В наших инструментах есть обработчик сбоев верхнего уровня (из события UnhandledException в AppDomain), который мы используем для отправки отчетов об ошибках с помощью минидампов. Он отлично работает. К сожалению, Задачи решают эту проблему.

Мы только начали использовать 4. 0 Tasks и обнаружил, что внутри кода выполнения действия Task есть try / catch, который захватывает исключение и сохраняет его для передачи вниз по цепочке задач. К сожалению, наличие catch (Exception) раскручивает стек, и когда мы создаем минидамп, сайт вызова теряется. Это означает, что у нас нет ни одной из локальных переменных во время сбоя, или они были собраны и т. Д.

Фильтры исключений кажутся правильным инструментом для этой работы. Мы могли бы обернуть некоторый код действия Task в фильтр с помощью метода расширения, а при возникновении исключения вызвать наш код обработчика сбоя, чтобы сообщить об ошибке с помощью минидампа. Однако мы не хотим делать это для каждого исключения, потому что в нашем собственном коде может быть try-catch, который игнорирует определенные исключения. Мы хотим создать отчет о сбое, только если catch в Task собирался его обработать.

Есть ли способ пройти по цепочке обработчиков try / catch? Думаю, если бы я мог это сделать, я мог бы идти вверх в поисках ловушек до тех пор, пока не попаду в Task, а затем запустить обработчик сбоев, если это правда.

(Это похоже на дальний план, но я решил, что все равно спрошу.)

Или, если у кого-то есть идеи получше, я хотел бы их услышать!

ОБНОВЛЕНИЕ

Я создал небольшой пример программы, демонстрирующей проблему. Приношу свои извинения, я постарался сделать его как можно короче, но он все равно большой. : /

В приведенном ниже примере вы можете #define USETASK или #define USEWORKITEM (или ни одного), чтобы проверить один из трех вариантов.

В non -async case и случай USEWORKITEM, генерируемый минидамп строится на месте вызова, именно так, как нам нужно. Я могу загрузить его в VS и (после некоторого просмотра, чтобы найти нужный поток), я вижу, что у меня есть моментальный снимок, сделанный на сайте вызова. Замечательно.

В случае USETASK моментальный снимок берется из потока финализатора, который очищает Задачу. Это происходит спустя много времени после того, как исключение было сгенерировано, и поэтому получение минидампа на этом этапе бесполезно. Я могу выполнить Wait () для задачи, чтобы исключение было обработано быстрее, или я могу получить доступ к его исключению напрямую, или я могу создать минидамп из оболочки вокруг самого TestCrash, но все они по-прежнему имеют ту же проблему: уже слишком поздно, потому что стек был раскручен до той или иной ловушки.

Обратите внимание, что я намеренно включил в TestCrash команду try / catch, чтобы продемонстрировать, как мы хотим, чтобы одни исключения обрабатывались нормально, а другие перехватывались. USEWORKITEM и неасинхронные варианты работают именно так, как нам нужно. Задачи почти все делают правильно! Если бы я мог каким-то образом использовать фильтр исключений, который позволяет мне подниматься по цепочке try / catch (без фактического раскручивания), пока я не попаду в ловушку внутри Task, я мог бы сам провести необходимые тесты, чтобы увидеть, нужно ли мне запускать обработчик сбоев или не. Отсюда мой первоначальный вопрос.

Вот образец.

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        AppDomain.CurrentDomain.UnhandledException += (_, __) =>
            {
                using (var stream = File.Create(@"c:\temp\test.dmp"))
                {
                    var process = Process.GetCurrentProcess();
                    MiniDumpWriteDump(
                        process.Handle,
                        process.Id,
                        stream.SafeFileHandle.DangerousGetHandle(),
                        MiniDumpType.MiniDumpWithFullMemory,
                        IntPtr.Zero,
                        IntPtr.Zero,
                        IntPtr.Zero);
                }
                Process.GetCurrentProcess().Kill();
            };
        TaskScheduler.UnobservedTaskException += (_, __) =>
            Debug.WriteLine("If this is called, the call site has already been lost!");

        // must be in separate func to permit collecting the task
        RunTest();

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

    static void RunTest()
    {

#if USETASK
        var t = new Task(TestCrash);
        t.RunSynchronously();
#elif USEWORKITEM
        var done = false;
        ThreadPool.QueueUserWorkItem(_ => { TestCrash(); done = true; });
        while (!done) { }
#else
        TestCrash();
#endif
    }

    static void TestCrash()
    {
        try
        {
            new WebClient().DownloadData("http://filenoexist");
        }
        catch (WebException)
        {
            Debug.WriteLine("Caught a WebException!");
        }
        throw new InvalidOperationException("test");
    }

    enum MiniDumpType
    {
        //...
        MiniDumpWithFullMemory = 0x00000002,
        //...
    }

    [DllImport("Dbghelp.dll")]
    static extern bool MiniDumpWriteDump(
        IntPtr hProcess,
        int processId,
        IntPtr hFile,
        MiniDumpType dumpType,
        IntPtr exceptionParam,
        IntPtr userStreamParam,
        IntPtr callbackParam);
}
15
задан scobi 11 May 2011 в 16:37
поделиться