Skip to content

Commit e939275

Browse files
committed
[MSBuildDeviceIntegration] Handle Debugger errors
We occasionally see the following errors on CI. ``` Unhandled error launching soft debugger System.AggregateException: One or more errors occurred. (DWP Handshake failed.) ---> System.IO.IOException: DWP Handshake failed. at Mono.Debugger.Soft.Connection.Connect() in /Users/builder/azdo/_work/1/s/xamarin-android/external/debugger-libs/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs:line 1361 at Mono.Debugger.Soft.VirtualMachine.connect() in /Users/builder/azdo/_work/1/s/xamarin-android/external/debugger-libs/Mono.Debugger.Soft/Mono.Debugger.Soft/VirtualMachine.cs:line 381 at Mono.Debugger.Soft.VirtualMachineManager.Connect(Connection transport, StreamReader standardOutput, StreamReader standardError) in /Users/builder/azdo/_work/1/s/xamarin-android/external/debugger-libs/Mono.Debugger.Soft/Mono.Debugger.Soft/VirtualMachineManager.cs:line 353 at Mono.Debugger.Soft.VirtualMachineManager.ConnectInternalAsync(Socket dbg_sock, Socket con_sock, IPEndPoint dbg_ep, IPEndPoint con_ep) in /Users/builder/azdo/_work/1/s/xamarin-android/external/debugger-libs/Mono.Debugger.Soft/Mono.Debugger.Soft/VirtualMachineManager.cs:line 309 --- End of inner exception stack trace --- ``` What is supposed to happen is the test shoud be retried. However according to the NUnit docs (1) the `Retry` attribute won't work if an unhandled exception occurs. So lets handle them, this way the `Retry` ca do its job and re-try the test. [1] https://docs.nunit.org/articles/nunit/writing-tests/attributes/retry.html
1 parent c4e09b6 commit e939275

File tree

2 files changed

+204
-188
lines changed

2 files changed

+204
-188
lines changed

tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs

Lines changed: 152 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -207,64 +207,69 @@ public override void OnCreate ()
207207
var sw = new Stopwatch ();
208208
// setup the debugger
209209
var session = new SoftDebuggerSession ();
210-
session.Breakpoints = new BreakpointStore ();
211-
string file = Path.Combine (Root, b.ProjectDirectory, "MainActivity.cs");
212-
int line = FindTextInFile (file, "base.OnCreate (bundle);");
213-
session.Breakpoints.Add (file, line);
214-
file = Path.Combine (Root, b.ProjectDirectory, "MyApplication.cs");
215-
line = FindTextInFile (file, "base.OnCreate ();");
216-
session.Breakpoints.Add (file, line);
217-
session.TargetHitBreakpoint += (sender, e) => {
218-
TestContext.WriteLine ($"BREAK {e.Type}, {e.Backtrace.GetFrame (0)}");
219-
breakcountHitCount++;
220-
session.Continue ();
221-
};
222-
var rnd = new Random ();
223-
int port = rnd.Next (10000, 20000);
224-
TestContext.Out.WriteLine ($"{port}");
225-
var args = new SoftDebuggerConnectArgs ("", IPAddress.Loopback, port) {
226-
MaxConnectionAttempts = 2000, // we need a long delay here to get a reliable connection
227-
};
228-
var startInfo = new SoftDebuggerStartInfo (args) {
229-
WorkingDirectory = Path.Combine (b.ProjectDirectory, proj.IntermediateOutputPath, "android", "assets"),
230-
};
231-
var options = new DebuggerSessionOptions () {
232-
EvaluationOptions = EvaluationOptions.DefaultOptions,
233-
};
234-
options.EvaluationOptions.UseExternalTypeResolver = true;
235-
RunProjectAndAssert (proj, b, doNotCleanupOnUpdate: true, parameters: new string [] {
236-
$"AndroidSdbTargetPort={port}",
237-
$"AndroidSdbHostPort={port}",
238-
"AndroidAttachDebugger=True",
239-
});
240-
241-
session.LogWriter += (isStderr, text) => { Console.WriteLine (text); };
242-
session.OutputWriter += (isStderr, text) => { Console.WriteLine (text); };
243-
session.DebugWriter += (level, category, message) => { Console.WriteLine (message); };
244-
// do we expect the app to start?
245-
Assert.AreEqual (activityStarts, WaitForDebuggerToStart (Path.Combine (Root, b.ProjectDirectory, "logcat.log")), "Debugger should have started");
246-
if (!activityStarts)
247-
return;
248-
Assert.False (session.HasExited, "Target should not have exited.");
249-
session.Run (startInfo, options);
250-
var expectedTime = TimeSpan.FromSeconds (1);
251-
var actualTime = ProfileFor (() => session.IsConnected);
252-
Assert.True (session.IsConnected, "Debugger should have connected but it did not.");
253-
TestContext.Out.WriteLine ($"Debugger connected in {actualTime}");
254-
Assert.LessOrEqual (actualTime, expectedTime, $"Debugger should have connected within {expectedTime} but it took {actualTime}.");
255-
// we need to wait here for a while to allow the breakpoints to hit
256-
// but we need to timeout
257-
TimeSpan timeout = TimeSpan.FromSeconds (60);
258-
while (session.IsConnected && breakcountHitCount < 2 && timeout >= TimeSpan.Zero) {
259-
Thread.Sleep (10);
260-
timeout = timeout.Subtract (TimeSpan.FromMilliseconds (10));
210+
try {
211+
session.Breakpoints = new BreakpointStore ();
212+
string file = Path.Combine (Root, b.ProjectDirectory, "MainActivity.cs");
213+
int line = FindTextInFile (file, "base.OnCreate (bundle);");
214+
session.Breakpoints.Add (file, line);
215+
file = Path.Combine (Root, b.ProjectDirectory, "MyApplication.cs");
216+
line = FindTextInFile (file, "base.OnCreate ();");
217+
session.Breakpoints.Add (file, line);
218+
session.TargetHitBreakpoint += (sender, e) => {
219+
TestContext.WriteLine ($"BREAK {e.Type}, {e.Backtrace.GetFrame (0)}");
220+
breakcountHitCount++;
221+
session.Continue ();
222+
};
223+
var rnd = new Random ();
224+
int port = rnd.Next (10000, 20000);
225+
TestContext.Out.WriteLine ($"{port}");
226+
var args = new SoftDebuggerConnectArgs ("", IPAddress.Loopback, port) {
227+
MaxConnectionAttempts = 2000, // we need a long delay here to get a reliable connection
228+
};
229+
var startInfo = new SoftDebuggerStartInfo (args) {
230+
WorkingDirectory = Path.Combine (b.ProjectDirectory, proj.IntermediateOutputPath, "android", "assets"),
231+
};
232+
var options = new DebuggerSessionOptions () {
233+
EvaluationOptions = EvaluationOptions.DefaultOptions,
234+
};
235+
options.EvaluationOptions.UseExternalTypeResolver = true;
236+
RunProjectAndAssert (proj, b, doNotCleanupOnUpdate: true, parameters: new string [] {
237+
$"AndroidSdbTargetPort={port}",
238+
$"AndroidSdbHostPort={port}",
239+
"AndroidAttachDebugger=True",
240+
});
241+
242+
session.LogWriter += (isStderr, text) => { Console.WriteLine (text); };
243+
session.OutputWriter += (isStderr, text) => { Console.WriteLine (text); };
244+
session.DebugWriter += (level, category, message) => { Console.WriteLine (message); };
245+
// do we expect the app to start?
246+
Assert.AreEqual (activityStarts, WaitForDebuggerToStart (Path.Combine (Root, b.ProjectDirectory, "logcat.log")), "Debugger should have started");
247+
if (!activityStarts)
248+
return;
249+
Assert.False (session.HasExited, "Target should not have exited.");
250+
session.Run (startInfo, options);
251+
var expectedTime = TimeSpan.FromSeconds (1);
252+
var actualTime = ProfileFor (() => session.IsConnected);
253+
Assert.True (session.IsConnected, "Debugger should have connected but it did not.");
254+
TestContext.Out.WriteLine ($"Debugger connected in {actualTime}");
255+
Assert.LessOrEqual (actualTime, expectedTime, $"Debugger should have connected within {expectedTime} but it took {actualTime}.");
256+
// we need to wait here for a while to allow the breakpoints to hit
257+
// but we need to timeout
258+
TimeSpan timeout = TimeSpan.FromSeconds (60);
259+
while (session.IsConnected && breakcountHitCount < 2 && timeout >= TimeSpan.Zero) {
260+
Thread.Sleep (10);
261+
timeout = timeout.Subtract (TimeSpan.FromMilliseconds (10));
262+
}
263+
WaitFor (2000);
264+
int expected = 2;
265+
Assert.AreEqual (expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
266+
b.BuildLogFile = "uninstall.log";
267+
Assert.True (b.Uninstall (proj), "Project should have uninstalled.");
268+
} catch (Exception ex) {
269+
Assert.Fail ($"Exception occurred {ex}");
270+
} finally {
271+
session.Exit ();
261272
}
262-
WaitFor (2000);
263-
int expected = 2;
264-
Assert.AreEqual (expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
265-
b.BuildLogFile = "uninstall.log";
266-
Assert.True (b.Uninstall (proj), "Project should have uninstalled.");
267-
session.Exit ();
268273
}
269274
}
270275

@@ -405,93 +410,97 @@ public Foo ()
405410
var sw = new Stopwatch ();
406411
// setup the debugger
407412
var session = new SoftDebuggerSession ();
408-
409-
session.Breakpoints = new BreakpointStore ();
410-
string file = Path.Combine (Root, appBuilder.ProjectDirectory, "MainActivity.cs");
411-
int line = FindTextInFile (file, "base.OnCreate (savedInstanceState);");
412-
session.Breakpoints.Add (file, line);
413-
414-
file = Path.Combine (Root, appBuilder.ProjectDirectory, "MainPage.xaml.cs");
415-
line = FindTextInFile (file, "InitializeComponent ();");
416-
session.Breakpoints.Add (file, line);
417-
418-
file = Path.Combine (Root, appBuilder.ProjectDirectory, "MainPage.xaml.cs");
419-
line = FindTextInFile (file, "Console.WriteLine (");
420-
session.Breakpoints.Add (file, line);
421-
422-
file = Path.Combine (Root, appBuilder.ProjectDirectory, "App.xaml.cs");
423-
line = FindTextInFile (file, "InitializeComponent ();");
424-
session.Breakpoints.Add (file, line);
425-
426-
file = Path.Combine (Root, libBuilder.ProjectDirectory, "Foo.cs");
427-
line = FindTextInFile (file, "public Foo ()");
428-
// Add one to the line so we get the '{' under the constructor
429-
session.Breakpoints.Add (file, line++);
430-
431-
session.TargetHitBreakpoint += (sender, e) => {
432-
TestContext.WriteLine ($"BREAK {e.Type}, {e.Backtrace.GetFrame (0)}");
433-
breakcountHitCount++;
434-
session.Continue ();
435-
};
436-
var rnd = new Random ();
437-
int port = rnd.Next (10000, 20000);
438-
TestContext.Out.WriteLine ($"{port}");
439-
var args = new SoftDebuggerConnectArgs ("", IPAddress.Loopback, port) {
440-
MaxConnectionAttempts = 2000,
441-
};
442-
var startInfo = new SoftDebuggerStartInfo (args) {
443-
WorkingDirectory = Path.Combine (appBuilder.ProjectDirectory, app.IntermediateOutputPath, "android", "assets"),
444-
};
445-
var options = new DebuggerSessionOptions () {
446-
EvaluationOptions = EvaluationOptions.DefaultOptions,
447-
};
448-
options.EvaluationOptions.UseExternalTypeResolver = true;
449-
450-
parameters.Add ($"AndroidSdbTargetPort={port}");
451-
parameters.Add ($"AndroidSdbHostPort={port}");
452-
parameters.Add ("AndroidAttachDebugger=True");
453-
454-
RunProjectAndAssert (app, appBuilder, doNotCleanupOnUpdate: true, parameters: parameters.ToArray ());
455-
456-
session.LogWriter += (isStderr, text) => {
457-
TestContext.Out.WriteLine (text);
458-
};
459-
session.OutputWriter += (isStderr, text) => {
460-
TestContext.Out.WriteLine (text);
461-
};
462-
session.DebugWriter += (level, category, message) => {
463-
TestContext.Out.WriteLine (message);
464-
};
465-
Assert.IsTrue (WaitForDebuggerToStart (Path.Combine (Root, appBuilder.ProjectDirectory, "logcat.log")), "Debugger should have started");
466-
session.Run (startInfo, options);
467-
TestContext.Out.WriteLine ($"Detected debugger startup in log");
468-
Assert.False (session.HasExited, "Target should not have exited.");
469-
WaitFor (TimeSpan.FromSeconds (30), () => session.IsConnected );
470-
Assert.True (session.IsConnected, "Debugger should have connected but it did not.");
471-
// we need to wait here for a while to allow the breakpoints to hit
472-
// but we need to timeout
473-
TestContext.Out.WriteLine ($"Debugger connected.");
474-
TimeSpan timeout = TimeSpan.FromSeconds (60);
475-
int expected = 4;
476-
while (session.IsConnected && breakcountHitCount < 3 && timeout >= TimeSpan.Zero) {
477-
Thread.Sleep (10);
478-
timeout = timeout.Subtract (TimeSpan.FromMilliseconds (10));
479-
}
480-
WaitFor (2000);
481-
Assert.AreEqual (expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
482-
breakcountHitCount = 0;
483-
ClearAdbLogcat ();
484-
ClearBlockingDialogs ();
485-
Assert.True (ClickButton (app.PackageName, "myXFButton", "CLICK ME"), "Button should have been clicked!");
486-
while (session.IsConnected && breakcountHitCount < 1 && timeout >= TimeSpan.Zero) {
487-
Thread.Sleep (10);
488-
timeout = timeout.Subtract (TimeSpan.FromMilliseconds (10));
413+
try {
414+
session.Breakpoints = new BreakpointStore ();
415+
string file = Path.Combine (Root, appBuilder.ProjectDirectory, "MainActivity.cs");
416+
int line = FindTextInFile (file, "base.OnCreate (savedInstanceState);");
417+
session.Breakpoints.Add (file, line);
418+
419+
file = Path.Combine (Root, appBuilder.ProjectDirectory, "MainPage.xaml.cs");
420+
line = FindTextInFile (file, "InitializeComponent ();");
421+
session.Breakpoints.Add (file, line);
422+
423+
file = Path.Combine (Root, appBuilder.ProjectDirectory, "MainPage.xaml.cs");
424+
line = FindTextInFile (file, "Console.WriteLine (");
425+
session.Breakpoints.Add (file, line);
426+
427+
file = Path.Combine (Root, appBuilder.ProjectDirectory, "App.xaml.cs");
428+
line = FindTextInFile (file, "InitializeComponent ();");
429+
session.Breakpoints.Add (file, line);
430+
431+
file = Path.Combine (Root, libBuilder.ProjectDirectory, "Foo.cs");
432+
line = FindTextInFile (file, "public Foo ()");
433+
// Add one to the line so we get the '{' under the constructor
434+
session.Breakpoints.Add (file, line++);
435+
436+
session.TargetHitBreakpoint += (sender, e) => {
437+
TestContext.WriteLine ($"BREAK {e.Type}, {e.Backtrace.GetFrame (0)}");
438+
breakcountHitCount++;
439+
session.Continue ();
440+
};
441+
var rnd = new Random ();
442+
int port = rnd.Next (10000, 20000);
443+
TestContext.Out.WriteLine ($"{port}");
444+
var args = new SoftDebuggerConnectArgs ("", IPAddress.Loopback, port) {
445+
MaxConnectionAttempts = 2000,
446+
};
447+
var startInfo = new SoftDebuggerStartInfo (args) {
448+
WorkingDirectory = Path.Combine (appBuilder.ProjectDirectory, app.IntermediateOutputPath, "android", "assets"),
449+
};
450+
var options = new DebuggerSessionOptions () {
451+
EvaluationOptions = EvaluationOptions.DefaultOptions,
452+
};
453+
options.EvaluationOptions.UseExternalTypeResolver = true;
454+
455+
parameters.Add ($"AndroidSdbTargetPort={port}");
456+
parameters.Add ($"AndroidSdbHostPort={port}");
457+
parameters.Add ("AndroidAttachDebugger=True");
458+
459+
RunProjectAndAssert (app, appBuilder, doNotCleanupOnUpdate: true, parameters: parameters.ToArray ());
460+
461+
session.LogWriter += (isStderr, text) => {
462+
TestContext.Out.WriteLine (text);
463+
};
464+
session.OutputWriter += (isStderr, text) => {
465+
TestContext.Out.WriteLine (text);
466+
};
467+
session.DebugWriter += (level, category, message) => {
468+
TestContext.Out.WriteLine (message);
469+
};
470+
Assert.IsTrue (WaitForDebuggerToStart (Path.Combine (Root, appBuilder.ProjectDirectory, "logcat.log")), "Debugger should have started");
471+
session.Run (startInfo, options);
472+
TestContext.Out.WriteLine ($"Detected debugger startup in log");
473+
Assert.False (session.HasExited, "Target should not have exited.");
474+
WaitFor (TimeSpan.FromSeconds (30), () => session.IsConnected );
475+
Assert.True (session.IsConnected, "Debugger should have connected but it did not.");
476+
// we need to wait here for a while to allow the breakpoints to hit
477+
// but we need to timeout
478+
TestContext.Out.WriteLine ($"Debugger connected.");
479+
TimeSpan timeout = TimeSpan.FromSeconds (60);
480+
int expected = 4;
481+
while (session.IsConnected && breakcountHitCount < 3 && timeout >= TimeSpan.Zero) {
482+
Thread.Sleep (10);
483+
timeout = timeout.Subtract (TimeSpan.FromMilliseconds (10));
484+
}
485+
WaitFor (2000);
486+
Assert.AreEqual (expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
487+
breakcountHitCount = 0;
488+
ClearAdbLogcat ();
489+
ClearBlockingDialogs ();
490+
Assert.True (ClickButton (app.PackageName, "myXFButton", "CLICK ME"), "Button should have been clicked!");
491+
while (session.IsConnected && breakcountHitCount < 1 && timeout >= TimeSpan.Zero) {
492+
Thread.Sleep (10);
493+
timeout = timeout.Subtract (TimeSpan.FromMilliseconds (10));
494+
}
495+
expected = 1;
496+
Assert.AreEqual (expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
497+
appBuilder.BuildLogFile = "uninstall.log";
498+
Assert.True (appBuilder.Uninstall (app), "Project should have uninstalled.");
499+
} catch (Exception ex) {
500+
Assert.Fail($"Exception occurred {ex}");
501+
} finally {
502+
session.Exit ();
489503
}
490-
expected = 1;
491-
Assert.AreEqual (expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
492-
appBuilder.BuildLogFile = "uninstall.log";
493-
Assert.True (appBuilder.Uninstall (app), "Project should have uninstalled.");
494-
session.Exit ();
495504
}
496505
}
497506
}

0 commit comments

Comments
 (0)