Fix 'async void' method stepping.
authorMikhail Kurinnoi <m.kurinnoi@samsung.com>
Mon, 4 Jul 2022 14:53:14 +0000 (17:53 +0300)
committerAlexander Soldatov/Platform Lab /SRR/Staff Engineer/Samsung Electronics <soldatov.a@samsung.com>
Mon, 18 Jul 2022 12:55:31 +0000 (15:55 +0300)
src/debugger/stepper_async.cpp
test-suite/MITestAsyncStepping/Program.cs
test-suite/MITestNoJMCAsyncStepping/Program.cs
test-suite/NetcoreDbgTest/ControlScript.cs
test-suite/VSCodeTestAsyncStepping/Program.cs
test-suite/VSCodeTestNoJMCAsyncStepping/Program.cs

index 00565d0ce383f477f304e8beb744d64cad19ef4d..079c0e7a4480d4e12810213a7cd4d41a5e85ef48 100644 (file)
@@ -6,6 +6,7 @@
 #include "debugger/stepper_async.h"\r
 #include "debugger/threads.h"\r
 #include "metadata/modules.h"\r
+#include "metadata/typeprinter.h"\r
 #include "debugger/evalhelpers.h"\r
 #include "debugger/valueprint.h"\r
 #include "utils/utf.h"\r
@@ -160,15 +161,13 @@ static HRESULT GetAsyncIdReference(ICorDebugThread *pThread, ICorDebugFrame *pFr
 // [in] pThread - managed thread for evaluation (related to pFrame);\r
 // [in] pFrame - frame that used for get all info needed (function, module, etc);\r
 // [in] pEvalHelpers - pointer to managed debugger EvalHelpers;\r
-static HRESULT SetNotificationForWaitCompletion(ICorDebugThread *pThread, ICorDebugFrame *pFrame, EvalHelpers *pEvalHelpers)\r
+static HRESULT SetNotificationForWaitCompletion(ICorDebugThread *pThread, ICorDebugFrame *pFrame, ICorDebugValue *pBuilderValue, EvalHelpers *pEvalHelpers)\r
 {\r
     HRESULT Status;\r
-    ToRelease<ICorDebugValue> pValue;\r
-    IfFailRet(GetAsyncTBuilder(pFrame, &pValue));\r
 \r
     // Find SetNotificationForWaitCompletion() method.\r
     ToRelease<ICorDebugValue2> pValue2;\r
-    IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));\r
+    IfFailRet(pBuilderValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));\r
     ToRelease<ICorDebugType> pType;\r
     IfFailRet(pValue2->GetExactType(&pType));\r
     ToRelease<ICorDebugClass> pClass;\r
@@ -238,7 +237,7 @@ static HRESULT SetNotificationForWaitCompletion(ICorDebugThread *pThread, ICorDe
     IfFailRet(pModule->GetFunctionFromToken(setNotifDef, &pFunc));\r
 \r
     ICorDebugType *ppArgsType[] = {pType, pNewBooleanType};\r
-    ICorDebugValue *ppArgsValue[] = {pValue, pNewBoolean};\r
+    ICorDebugValue *ppArgsValue[] = {pBuilderValue, pNewBoolean};\r
     IfFailRet(pEvalHelpers->EvalFunction(pThread, pFunc, ppArgsType, 2, ppArgsValue, 2, nullptr, defaultEvalFlags));\r
 \r
     return S_OK;\r
@@ -287,7 +286,18 @@ HRESULT AsyncStepper::SetupStep(ICorDebugThread *pThread, IDebugger::StepType st
     }\r
     if (stepType == IDebugger::StepType::STEP_OUT)\r
     {\r
-        IfFailRet(SetNotificationForWaitCompletion(pThread, pFrame, m_sharedEvalHelpers.get()));\r
+        ToRelease<ICorDebugValue> pBuilderValue;\r
+        IfFailRet(GetAsyncTBuilder(pFrame, &pBuilderValue));\r
+\r
+        // In case method is "async void", builder is "System.Runtime.CompilerServices.AsyncVoidMethodBuilder"\r
+        // "If we are inside `async void` method, do normal step-out" from:\r
+        // https://github.com/dotnet/runtime/blob/32d0360b73bd77256cc9a9314a3c4280a61ea9bc/src/mono/mono/component/debugger-engine.c#L1350\r
+        std::string builderType;\r
+        IfFailRet(TypePrinter::GetTypeOfValue(pBuilderValue, builderType));\r
+        if (builderType == "System.Runtime.CompilerServices.AsyncVoidMethodBuilder")\r
+            return m_simpleStepper->SetupStep(pThread, IDebugger::StepType::STEP_OUT);\r
+\r
+        IfFailRet(SetNotificationForWaitCompletion(pThread, pFrame, pBuilderValue, m_sharedEvalHelpers.get()));\r
         IfFailRet(SetBreakpointIntoNotifyDebuggerOfWaitCompletion());\r
         // Note, we don't create stepper here, since all we need in case of breakpoint is call Continue() from StepCommand().\r
         return S_OK;\r
index fd62efa27dc1d5d9366231145005d7da5211be6b..4df572ec9deb79e49b7df4c028d816280298f90b 100644 (file)
@@ -124,6 +124,45 @@ namespace NetcoreDbgTest.Script
                          @"__FILE__:__LINE__"+"\n"+caller_trace);
         }
 
+        public void WasBreakpointHit(string caller_trace, string bpName)
+        {
+            var bp = (LineBreakpoint)ControlInfo.Breakpoints[bpName];
+
+            Func<MIOutOfBandRecord, bool> filter = (record) => {
+                if (!IsStoppedEvent(record)) {
+                    return false;
+                }
+
+                var output = ((MIAsyncRecord)record).Output;
+                var reason = (MIConst)output["reason"];
+
+                if (reason.CString != "breakpoint-hit") {
+                    return false;
+                }
+
+                var frame = (MITuple)output["frame"];
+                var fileName = (MIConst)frame["file"];
+                var line = ((MIConst)frame["line"]).Int;
+
+                if (fileName.CString == bp.FileName &&
+                    line == bp.NumLine) {
+                    return true;
+                }
+
+                return false;
+            };
+
+            Assert.True(MIDebugger.IsEventReceived(filter),
+                        @"__FILE__:__LINE__"+"\n"+caller_trace);
+        }
+
+        public void Continue(string caller_trace)
+        {
+            Assert.Equal(MIResultClass.Running,
+                         MIDebugger.Request("-exec-continue").Class,
+                         @"__FILE__:__LINE__"+"\n"+caller_trace);
+        }
+
         public void DebuggerExit(string caller_trace)
         {
             Assert.Equal(MIResultClass.Exit,
@@ -295,13 +334,35 @@ namespace MITestAsyncStepping
             ctest_attr2.test_func();                                        Label.Breakpoint("test_attr_class2_func");
             Console.WriteLine("Test debugger attribute on methods end.");   Label.Breakpoint("test_attr_end");
 
-            Label.Checkpoint("test_attr2", "finish", (Object context) => {
+            Label.Checkpoint("test_attr2", "test_async_void", (Object context) => {
                 Context Context = (Context)context;
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_class1_func");
                 Context.StepIn(@"__FILE__:__LINE__");
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_class2_func");
                 Context.StepIn(@"__FILE__:__LINE__");
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_end");
+
+                Context.EnableBreakpoint(@"__FILE__:__LINE__", "test_async_void1");
+                Context.EnableBreakpoint(@"__FILE__:__LINE__", "test_async_void3");
+                Context.Continue(@"__FILE__:__LINE__");
+            });
+
+            // Test `async void` stepping.
+
+            await Task.Run((Action)( async () =>
+            {
+                await Task.Yield();                                        Label.Breakpoint("test_async_void1");
+            }));                                                           Label.Breakpoint("test_async_void2");
+            await Task.Delay(5000);
+            Console.WriteLine("Test debugger `async void` stepping end."); Label.Breakpoint("test_async_void3");
+
+            Label.Checkpoint("test_async_void", "finish", (Object context) => {
+                Context Context = (Context)context;
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "test_async_void1");
+                Context.StepOver(@"__FILE__:__LINE__");
+                Context.WasStep(@"__FILE__:__LINE__", "test_async_void2");
+                Context.StepOver(@"__FILE__:__LINE__");
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "test_async_void3");
                 Context.StepOut(@"__FILE__:__LINE__");
             });
 
index ed9ea9490a08e69e8c50ea596ba3fb69e4d35393..926c7fe00d2e89d7214d48f7013d8bd12e1b3621 100644 (file)
@@ -85,6 +85,58 @@ namespace NetcoreDbgTest.Script
             Assert.True(MIDebugger.IsEventReceived(filter), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
         }\r
 \r
+        public void EnableBreakpoint(string caller_trace, string bpName)\r
+        {\r
+            Breakpoint bp = ControlInfo.Breakpoints[bpName];\r
+\r
+            Assert.Equal(BreakpointType.Line, bp.Type, @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+\r
+            var lbp = (LineBreakpoint)bp;\r
+\r
+            Assert.Equal(MIResultClass.Done,\r
+                         MIDebugger.Request("-break-insert -f " + lbp.FileName + ":" + lbp.NumLine).Class,\r
+                         @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+        }\r
+\r
+        public void WasBreakpointHit(string caller_trace, string bpName)\r
+        {\r
+            var bp = (LineBreakpoint)ControlInfo.Breakpoints[bpName];\r
+\r
+            Func<MIOutOfBandRecord, bool> filter = (record) => {\r
+                if (!IsStoppedEvent(record)) {\r
+                    return false;\r
+                }\r
+\r
+                var output = ((MIAsyncRecord)record).Output;\r
+                var reason = (MIConst)output["reason"];\r
+\r
+                if (reason.CString != "breakpoint-hit") {\r
+                    return false;\r
+                }\r
+\r
+                var frame = (MITuple)output["frame"];\r
+                var fileName = (MIConst)frame["file"];\r
+                var line = ((MIConst)frame["line"]).Int;\r
+\r
+                if (fileName.CString == bp.FileName &&\r
+                    line == bp.NumLine) {\r
+                    return true;\r
+                }\r
+\r
+                return false;\r
+            };\r
+\r
+            Assert.True(MIDebugger.IsEventReceived(filter),\r
+                        @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+        }\r
+\r
+        public void Continue(string caller_trace)\r
+        {\r
+            Assert.Equal(MIResultClass.Running,\r
+                         MIDebugger.Request("-exec-continue").Class,\r
+                         @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+        }\r
+\r
         public void WasExit(string caller_trace)\r
         {\r
             Func<MIOutOfBandRecord, bool> filter = (record) => {\r
@@ -204,7 +256,7 @@ namespace MITestNoJMCAsyncStepping
             await ctest_attr2.test_func();                                  Label.Breakpoint("test_attr_class2_func");\r
             Console.WriteLine("Test debugger attribute on methods end.");   Label.Breakpoint("test_attr_end");\r
 \r
-            Label.Checkpoint("test_attr2", "finish", (Object context) => {\r
+            Label.Checkpoint("test_attr2", "test_async_void", (Object context) => {\r
                 Context Context = (Context)context;\r
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_class1_func");\r
                 Context.StepIn(@"__FILE__:__LINE__");\r
@@ -213,6 +265,28 @@ namespace MITestNoJMCAsyncStepping
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_class2_func_in");\r
                 Context.StepOut(@"__FILE__:__LINE__");\r
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_end");\r
+\r
+                Context.EnableBreakpoint(@"__FILE__:__LINE__", "test_async_void1");\r
+                Context.EnableBreakpoint(@"__FILE__:__LINE__", "test_async_void3");\r
+                Context.Continue(@"__FILE__:__LINE__");\r
+            });\r
+\r
+            // Test `async void` stepping.\r
+\r
+            await Task.Run((Action)( async () =>\r
+            {\r
+                await Task.Yield();                                        Label.Breakpoint("test_async_void1");\r
+            }));                                                           Label.Breakpoint("test_async_void2");\r
+            await Task.Delay(5000);\r
+            Console.WriteLine("Test debugger `async void` stepping end."); Label.Breakpoint("test_async_void3");\r
+\r
+            Label.Checkpoint("test_async_void", "finish", (Object context) => {\r
+                Context Context = (Context)context;\r
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "test_async_void1");\r
+                Context.StepOver(@"__FILE__:__LINE__");\r
+                Context.WasStep(@"__FILE__:__LINE__", "test_async_void2");\r
+                Context.StepOver(@"__FILE__:__LINE__");\r
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "test_async_void3");\r
                 Context.StepOut(@"__FILE__:__LINE__");\r
             });\r
 \r
index be4bae50822e4e615885551a7f56cd74b9f95c28..d3c4890382b51fc75b84ba1646028c7583289c80 100644 (file)
@@ -285,6 +285,8 @@ namespace NetcoreDbgTestCore
                         id, new Tuple<string, InvocationExpressionSyntax>(next_id, node));
                     break;
             }
+
+            base.VisitInvocationExpression(node);
         }
 
         public TestLabelsInfo TestLabelsInfo;
index 2afdb442f86eb4d70feb9bb38328f37f681b4a73..277b66d9aeadddbd1b957ab157507a7fce845a46 100644 (file)
@@ -157,6 +157,50 @@ namespace NetcoreDbgTest.Script
             throw new ResultNotSuccessException(@"__FILE__:__LINE__"+"\n"+caller_trace);
         }
 
+        public void WasBreakpointHit(string caller_trace, string bpName)
+        {
+            Func<string, bool> filter = (resJSON) => {
+                if (VSCodeDebugger.isResponseContainProperty(resJSON, "event", "stopped")
+                    && VSCodeDebugger.isResponseContainProperty(resJSON, "reason", "breakpoint")) {
+                    threadId = Convert.ToInt32(VSCodeDebugger.GetResponsePropertyValue(resJSON, "threadId"));
+                    return true;
+                }
+                return false;
+            };
+
+            Assert.True(VSCodeDebugger.IsEventReceived(filter), @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+            StackTraceRequest stackTraceRequest = new StackTraceRequest();
+            stackTraceRequest.arguments.threadId = threadId;
+            stackTraceRequest.arguments.startFrame = 0;
+            stackTraceRequest.arguments.levels = 20;
+            var ret = VSCodeDebugger.Request(stackTraceRequest);
+            Assert.True(ret.Success, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+            Breakpoint breakpoint = ControlInfo.Breakpoints[bpName];
+            Assert.Equal(BreakpointType.Line, breakpoint.Type, @"__FILE__:__LINE__"+"\n"+caller_trace);
+            var lbp = (LineBreakpoint)breakpoint;
+
+            StackTraceResponse stackTraceResponse =
+                JsonConvert.DeserializeObject<StackTraceResponse>(ret.ResponseStr);
+
+            if (stackTraceResponse.body.stackFrames[0].line == lbp.NumLine
+                && stackTraceResponse.body.stackFrames[0].source.name == lbp.FileName
+                // NOTE this code works only with one source file
+                && stackTraceResponse.body.stackFrames[0].source.path == ControlInfo.SourceFilesPath)
+                return;
+
+            throw new ResultNotSuccessException(@"__FILE__:__LINE__"+"\n"+caller_trace);
+        }
+
+        public void Continue(string caller_trace)
+        {
+            ContinueRequest continueRequest = new ContinueRequest();
+            continueRequest.arguments.threadId = threadId;
+            Assert.True(VSCodeDebugger.Request(continueRequest).Success,
+                        @"__FILE__:__LINE__"+"\n"+caller_trace);
+        }
+
         public void StepOver(string caller_trace)
         {
             NextRequest nextRequest = new NextRequest();
@@ -309,13 +353,36 @@ namespace VSCodeTestAsyncStepping
             await ctest_attr2.test_func();                                  Label.Breakpoint("test_attr_class2_func");
             Console.WriteLine("Test debugger attribute on methods end.");   Label.Breakpoint("test_attr_end");
 
-            Label.Checkpoint("test_attr2", "finish", (Object context) => {
+            Label.Checkpoint("test_attr2", "test_async_void", (Object context) => {
                 Context Context = (Context)context;
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_class1_func");
                 Context.StepIn(@"__FILE__:__LINE__");
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_class2_func");
                 Context.StepIn(@"__FILE__:__LINE__");
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_end");
+
+                Context.AddBreakpoint(@"__FILE__:__LINE__", "test_async_void1");
+                Context.AddBreakpoint(@"__FILE__:__LINE__", "test_async_void3");
+                Context.SetBreakpoints(@"__FILE__:__LINE__");
+                Context.Continue(@"__FILE__:__LINE__");
+            });
+
+            // Test `async void` stepping.
+
+            await Task.Run((Action)( async () =>
+            {
+                await Task.Yield();                                        Label.Breakpoint("test_async_void1");
+            }));                                                           Label.Breakpoint("test_async_void2");
+            await Task.Delay(5000);
+            Console.WriteLine("Test debugger `async void` stepping end."); Label.Breakpoint("test_async_void3");
+
+            Label.Checkpoint("test_async_void", "finish", (Object context) => {
+                Context Context = (Context)context;
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "test_async_void1");
+                Context.StepOver(@"__FILE__:__LINE__");
+                Context.WasStep(@"__FILE__:__LINE__", "test_async_void2");
+                Context.StepOver(@"__FILE__:__LINE__");
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "test_async_void3");
                 Context.StepOut(@"__FILE__:__LINE__");
             });
 
index 9ae12b68d811f9cbda112e0af428ac505b2365c4..d70ac4e7e94f3a42ddbccc92e88bf02aa0a6017e 100644 (file)
@@ -134,6 +134,73 @@ namespace NetcoreDbgTest.Script
             throw new ResultNotSuccessException(@"__FILE__:__LINE__"+"\n"+caller_trace);\r
         }\r
 \r
+        public void AddBreakpoint(string caller_trace, string bpName, string Condition = null)\r
+        {\r
+            Breakpoint bp = ControlInfo.Breakpoints[bpName];\r
+            Assert.Equal(BreakpointType.Line, bp.Type, @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+            var lbp = (LineBreakpoint)bp;\r
+\r
+            BreakpointSourceName = lbp.FileName;\r
+            BreakpointList.Add(new SourceBreakpoint(lbp.NumLine, Condition));\r
+            BreakpointLines.Add(lbp.NumLine);\r
+        }\r
+\r
+        public void SetBreakpoints(string caller_trace)\r
+        {\r
+            SetBreakpointsRequest setBreakpointsRequest = new SetBreakpointsRequest();\r
+            setBreakpointsRequest.arguments.source.name = BreakpointSourceName;\r
+            // NOTE this code works only with one source file\r
+            setBreakpointsRequest.arguments.source.path = ControlInfo.SourceFilesPath;\r
+            setBreakpointsRequest.arguments.lines.AddRange(BreakpointLines);\r
+            setBreakpointsRequest.arguments.breakpoints.AddRange(BreakpointList);\r
+            setBreakpointsRequest.arguments.sourceModified = false;\r
+            Assert.True(VSCodeDebugger.Request(setBreakpointsRequest).Success, @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+        }\r
+\r
+        public void WasBreakpointHit(string caller_trace, string bpName)\r
+        {\r
+            Func<string, bool> filter = (resJSON) => {\r
+                if (VSCodeDebugger.isResponseContainProperty(resJSON, "event", "stopped")\r
+                    && VSCodeDebugger.isResponseContainProperty(resJSON, "reason", "breakpoint")) {\r
+                    threadId = Convert.ToInt32(VSCodeDebugger.GetResponsePropertyValue(resJSON, "threadId"));\r
+                    return true;\r
+                }\r
+                return false;\r
+            };\r
+\r
+            Assert.True(VSCodeDebugger.IsEventReceived(filter), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+\r
+            StackTraceRequest stackTraceRequest = new StackTraceRequest();\r
+            stackTraceRequest.arguments.threadId = threadId;\r
+            stackTraceRequest.arguments.startFrame = 0;\r
+            stackTraceRequest.arguments.levels = 20;\r
+            var ret = VSCodeDebugger.Request(stackTraceRequest);\r
+            Assert.True(ret.Success, @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+\r
+            Breakpoint breakpoint = ControlInfo.Breakpoints[bpName];\r
+            Assert.Equal(BreakpointType.Line, breakpoint.Type, @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+            var lbp = (LineBreakpoint)breakpoint;\r
+\r
+            StackTraceResponse stackTraceResponse =\r
+                JsonConvert.DeserializeObject<StackTraceResponse>(ret.ResponseStr);\r
+\r
+            if (stackTraceResponse.body.stackFrames[0].line == lbp.NumLine\r
+                && stackTraceResponse.body.stackFrames[0].source.name == lbp.FileName\r
+                // NOTE this code works only with one source file\r
+                && stackTraceResponse.body.stackFrames[0].source.path == ControlInfo.SourceFilesPath)\r
+                return;\r
+\r
+            throw new ResultNotSuccessException(@"__FILE__:__LINE__"+"\n"+caller_trace);\r
+        }\r
+\r
+        public void Continue(string caller_trace)\r
+        {\r
+            ContinueRequest continueRequest = new ContinueRequest();\r
+            continueRequest.arguments.threadId = threadId;\r
+            Assert.True(VSCodeDebugger.Request(continueRequest).Success,\r
+                        @"__FILE__:__LINE__"+"\n"+caller_trace);\r
+        }\r
+\r
         public void StepOver(string caller_trace)\r
         {\r
             NextRequest nextRequest = new NextRequest();\r
@@ -164,6 +231,9 @@ namespace NetcoreDbgTest.Script
         ControlInfo ControlInfo;\r
         VSCodeDebugger VSCodeDebugger;\r
         int threadId = -1;\r
+        string BreakpointSourceName;\r
+        List<SourceBreakpoint> BreakpointList = new List<SourceBreakpoint>();\r
+        List<int> BreakpointLines = new List<int>();\r
     }\r
 }\r
 \r
@@ -205,7 +275,7 @@ namespace VSCodeTestNoJMCAsyncStepping
             await ctest_attr2.test_func();                                  Label.Breakpoint("test_attr_class2_func");\r
             Console.WriteLine("Test debugger attribute on methods end.");   Label.Breakpoint("test_attr_end");\r
 \r
-            Label.Checkpoint("test_attr2", "finish", (Object context) => {\r
+            Label.Checkpoint("test_attr2", "test_async_void", (Object context) => {\r
                 Context Context = (Context)context;\r
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_class1_func");\r
                 Context.StepIn(@"__FILE__:__LINE__");\r
@@ -214,6 +284,29 @@ namespace VSCodeTestNoJMCAsyncStepping
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_class2_func_in");\r
                 Context.StepOut(@"__FILE__:__LINE__");\r
                 Context.WasStep(@"__FILE__:__LINE__", "test_attr_end");\r
+\r
+                Context.AddBreakpoint(@"__FILE__:__LINE__", "test_async_void1");\r
+                Context.AddBreakpoint(@"__FILE__:__LINE__", "test_async_void3");\r
+                Context.SetBreakpoints(@"__FILE__:__LINE__");\r
+                Context.Continue(@"__FILE__:__LINE__");\r
+            });\r
+\r
+            // Test `async void` stepping.\r
+\r
+            await Task.Run((Action)( async () =>\r
+            {\r
+                await Task.Yield();                                        Label.Breakpoint("test_async_void1");\r
+            }));                                                           Label.Breakpoint("test_async_void2");\r
+            await Task.Delay(5000);\r
+            Console.WriteLine("Test debugger `async void` stepping end."); Label.Breakpoint("test_async_void3");\r
+\r
+            Label.Checkpoint("test_async_void", "finish", (Object context) => {\r
+                Context Context = (Context)context;\r
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "test_async_void1");\r
+                Context.StepOver(@"__FILE__:__LINE__");\r
+                Context.WasStep(@"__FILE__:__LINE__", "test_async_void2");\r
+                Context.StepOver(@"__FILE__:__LINE__");\r
+                Context.WasBreakpointHit(@"__FILE__:__LINE__", "test_async_void3");\r
                 Context.StepOut(@"__FILE__:__LINE__");\r
             });\r
 \r