sco*_*obi 15 c# task-parallel-library
我们遇到了混乱任务与我们的顶级崩溃处理程序,并试图找到一个解决方法.我希望有人有一些想法.
我们的工具有一个顶级崩溃处理程序(来自AppDomain的UnhandledException事件),我们用它来用minidump提交bug报告.它运作得很好.不幸的是,任务在这方面引起了轰动.
我们刚刚开始使用4.0任务,并在Task动作执行代码内部发现了一个try/catch,它抓取异常并存储它以传递任务链.不幸的是,存在catch (Exception)放松堆栈,当我们创建minidump时,调用站点丢失了.这意味着我们在崩溃时没有任何局部变量,或者它们已被收集,等等.
异常过滤器似乎是这项工作的正确工具.我们可以通过扩展方法将一些Task动作代码包装在过滤器中,并且在抛出异常时调用我们的崩溃处理程序代码来报告minidump的错误.但是,我们不希望在每个异常上执行此操作,因为在我们自己的代码中可能存在忽略特定异常的try-catch.如果catchin Task要处理它,我们只想做崩溃报告.
有没有办法走向try/catch处理程序链?我想如果我能做到这一点,我可以向上走,寻找捕获,直到命中任务,然后触发崩溃处理程序,如果是真的.
(这似乎是一个很长的镜头,但我想我还是会问.)
或者,如果有人有任何更好的想法,我很乐意听到他们!
UPDATE
我创建了一个小示例程序来演示这个问题.我道歉,我试图让它尽可能短,但它仍然很大.:/
在下面的示例中,您可以#define USETASK或#define USEWORKITEM(或没有)测试三个选项中的一个.
在非异步情况和USEWORKITEM情况下,生成的minidump是在调用站点构建的,正是我们需要的.我可以在VS中加载它(在一些浏览后找到正确的线程),我看到我在呼叫站点拍摄了快照.真棒.
在USETASK案例中,快照从终结器线程中获取,该线程正在清理任务.抛出异常后很长时间,因此在此时抓取一个minidump是没用的.我可以对任务执行Wait()以更快地处理异常,或者我可以直接访问它的Exception,或者我可以在TestCrash本身的包装器中创建minidump,但所有这些仍然有相同的问题:这太晚了,因为堆栈已经解开了一个或另一个.
请注意,我故意在TestCrash中放置一个try/catch来演示我们希望如何正常处理某些异常,以及其他异常被捕获.USEWORKITEM和非异步案例正是我们所需要的.任务几乎做得对!如果我能以某种方式使用异常过滤器让我走向try/catch链(实际上没有展开),直到我点击Task内部的catch,我可以自己做必要的测试,看看是否需要运行崩溃处理程序或不.因此,我原来的问题.
这是样本.
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);
}
Run Code Online (Sandbox Code Playgroud)
听起来好像你可以将顶级任务包装在异常过滤器(用VB.NET编写?)中,你就可以做你想做的事.由于您的过滤器将在Task自己的异常过滤器之前运行,因此只有在您的任务中没有其他任何内容处理异常但在Task获得它之前,它才会被调用.
这是一个工作样本.ExceptionFilter在VB文件中创建一个用这个调用的VB库项目:
Imports System.IO
Imports System.Diagnostics
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
Public Module ExceptionFilter
Private Enum MINIDUMP_TYPE
MiniDumpWithFullMemory = 2
End Enum
<DllImport("dbghelp.dll")>
Private Function MiniDumpWriteDump(
ByVal hProcess As IntPtr,
ByVal ProcessId As Int32,
ByVal hFile As IntPtr,
ByVal DumpType As MINIDUMP_TYPE,
ByVal ExceptionParam As IntPtr,
ByVal UserStreamParam As IntPtr,
ByVal CallackParam As IntPtr) As Boolean
End Function
Function FailFastFilter() As Boolean
Dim proc = Process.GetCurrentProcess()
Using stream As FileStream = File.Create("C:\temp\test.dmp")
MiniDumpWriteDump(proc.Handle, proc.Id, stream.SafeFileHandle.DangerousGetHandle(),
MINIDUMP_TYPE.MiniDumpWithFullMemory, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)
End Using
proc.Kill()
Return False
End Function
<Extension()>
Public Function CrashFilter(ByVal task As Action) As Action
Return Sub()
Try
task()
Catch ex As Exception When _
FailFastFilter()
End Try
End Sub
End Function
End Module
Run Code Online (Sandbox Code Playgroud)
然后创建一个C#项目并添加一个引用ExceptionFilter.这是我用过的程序:
using System;
using System.Diagnostics;
using System.Net;
using System.Threading.Tasks;
using ExceptionFilter;
class Program
{
static void Main()
{
new Task(new Action(TestCrash).CrashFilter()).RunSynchronously();
}
static void TestCrash()
{
try
{
new WebClient().DownloadData("http://filenoexist");
}
catch (WebException)
{
Debug.WriteLine("Caught a WebException!");
}
throw new InvalidOperationException("test");
}
}
Run Code Online (Sandbox Code Playgroud)
我运行了C#程序,打开了DMP文件,并检出了调用堆栈.该TestCrash函数在堆栈上(几帧向上)throw new作为当前行.
仅供参考,我想我会使用Environment.FailFast()你的minidump/kill操作,但这在你的工作流程中可能不会有效.
| 归档时间: |
|
| 查看次数: |
1696 次 |
| 最近记录: |