#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
// [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
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
}\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
@"__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,
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__");
});
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
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
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
id, new Tuple<string, InvocationExpressionSyntax>(next_id, node));
break;
}
+
+ base.VisitInvocationExpression(node);
}
public TestLabelsInfo TestLabelsInfo;
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();
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__");
});
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
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
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
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