emgucv / emgucv

Emgu CV is a cross platform .Net wrapper to the OpenCV image processing library.
https://www.emgu.com/
Other
2.12k stars 581 forks source link

[BUG] Exceptions thrown by CvErrorHandler cannot be handled under Linux and result in application crash #867

Open ghost opened 1 year ago

ghost commented 1 year ago

Describe the bug Exceptions thrown by OpenCV are handled by CvErrorHandler which throws a CvException in managed code. On windows systems, this exeption can be caught. On our linux system however, this results in an application crash. Further, the stack trace is unusable.

Resulting stack trace on windows for minimal example:

Emgu.CV.Util.CvException: OpenCV: total >= 0 && (depth == CV_32F || depth == CV_32S)
   at Emgu.CV.CvInvoke.CvErrorHandler(Int32 status, IntPtr funcName, IntPtr errMsg, IntPtr fileName, Int32 line, IntPtr userData)
   at Emgu.CV.CvInvoke.cveConvexHull(IntPtr points, IntPtr hull, Boolean clockwise, Boolean returnPoints)
   at Emgu.CV.CvInvoke.ConvexHull(IInputArray points, IOutputArray hull, Boolean clockwise, Boolean returnPoints)
   at Mabri.Module.P126.Embedded.Common.Exceptions.EmguExceptionHandlingTest.ProvokeEmguException2() in C:\Users\Fabian\source\repos\Mabri.Module.P126.Embedded\Mabri.Module.P126.Embedded.Common\Exceptions\EmguExceptionHandlingTest.cs:line 24

Resulting stack trace on Raspberry Pi OS for minimal example:

Der aktive Testlauf wurde abgebrochen. Grund: Der Testhostprozess ist abgestürzt. : Unhandled exception. Emgu.CV.Util.CvException: OpenCV: total >= 0 && (depth == CV_32F || depth == CV_32S)
   at Emgu.CV.CvInvoke.CvErrorHandler(Int32 status, IntPtr funcName, IntPtr errMsg, IntPtr fileName, Int32 line, IntPtr userData)

Der Testlauf wurde mit dem Fehler System.Exception: One or more errors occurred.
 ---> System.Exception: Unable to read beyond the end of the stream.
   at System.IO.BinaryReader.Read7BitEncodedInt()
   at System.IO.BinaryReader.ReadString()
   at Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.LengthPrefixCommunicationChannel.NotifyDataAvailable()
   at Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.TcpClientExtensions.MessageLoopAsync(TcpClient client, ICommunicationChannel channel, Action1 errorHandler, CancellationToken cancellationToken)
   --- End of inner exception stack trace --- abgebrochen.

OS / Platform Windows 10 works fine, Raspberry Pi OS results in a crash

.Net version DotNet 6

CPU Architecture x64 vs. ARM64

Emgu CV package used Emgu.CV 4.5.5.4894 from official NuGet repo Emgu.CV.runtime.windows 4.5.5.4823 from official NuGet repo Emgu.CV.runtime.debian-arm64 4.5.5.4894 cloned from Emgu git and self-compiled, deployed as NuGet package

To Reproduce

[Test]
  public void ProvokeEmguException2()
  {
    var emptyContour = new VectorOfPoint();
    var hull = new Mat();
    try
    {
      CvInvoke.ConvexHull(emptyContour, hull);
    }
    catch (Exception ex)
    {
      TestContext.Error.WriteLine(ex);
    }
  }

Expected behavior ConvexHull should throw an exception because the contour is empty. The CvException should be caught on both systems when executing the unit test. The CvException should contain a usable stack trace.

Additional context As said above, we compiled the runtime NuGet for debian-arm64 by ourselves. We suspect that this could be part of the problem. Back then, we had to compile the NuGet ourselves however. If there is an official NuGet working with Raspberry Pi OS, please tell me and I will try again.

ghost commented 1 year ago

Update: I just tried the same code using only the official NuGet packages 4.7.0.5276 with the same result.

Code:

using Emgu.CV.Util;
using Emgu.CV;

var emptyContour = new VectorOfPoint();
var hull = new Mat();
try
{
  CvInvoke.ConvexHull(emptyContour, hull);
}
catch (Exception ex)
{
  Console.WriteLine(ex);
}

Project:

  <ItemGroup>
    <PackageReference Include="Emgu.CV" Version="4.7.0.5276" />
    <PackageReference Include="Emgu.CV.runtime.debian-arm64" Version="4.7.0.5276" />
    <PackageReference Include="Emgu.CV.runtime.windows" Version="4.7.0.5276" />
  </ItemGroup>

Result on Windows:

Emgu.CV.Util.CvException: OpenCV: total >= 0 && (depth == CV_32F || depth == CV_32S)
   at Emgu.CV.CvInvoke.CvErrorHandler(Int32 status, IntPtr funcName, IntPtr errMsg, IntPtr fileName, Int32 line, IntPtr userData)
   at Emgu.CV.CvInvoke.cveConvexHull(IntPtr points, IntPtr hull, Boolean clockwise, Boolean returnPoints)
   at Emgu.CV.CvInvoke.ConvexHull(IInputArray points, IOutputArray hull, Boolean clockwise, Boolean returnPoints)
   at Program.<Main>$(String[] args) in C:\Users\Fabian\source\repos\EmguExceptionTest\EmguExceptionTest\Program.cs:line 8

C:\Users\Fabian\source\repos\EmguExceptionTest\EmguExceptionTest\bin\Debug\net6.0\EmguExceptionTest.exe (process 33684) exited with code 0.

Result on Linux:

dotnet EmguExceptionTest/bin/Release/net6.0/EmguExceptionTest.dll
Unhandled exception. Emgu.CV.Util.CvException: OpenCV: total >= 0 && (depth == CV_32F || depth == CV_32S)
   at Emgu.CV.CvInvoke.CvErrorHandler(Int32 status, IntPtr funcName, IntPtr errMsg, IntPtr fileName, Int32 line, IntPtr userData)
Abgebrochen
emgucv commented 1 year ago

Is the rapsberry pi unit test run directly from raspberry pi from a console? Or is it running from visual studio using remote debugging? Based on the debug message, it seems that the unit test is run remotely instead of locally from Raspberry Pi.

emgucv commented 1 year ago

Looks like the second test is run directly on Raspberry Pi. We will take a look.

ghost commented 1 year ago

Is the rapsberry pi unit test run directly from raspberry pi from a console? Or is it running from visual studio using remote debugging? Based on the debug message, it seems that the unit test is run remotely instead of locally from Raspberry Pi.

The test was run on the Raspberry using dotnet test. Thanks for investigating!

ghost commented 1 year ago

Hello, are there any updates regarding this issue?

emgucv commented 1 year ago

I can confirm that it still existing for dotnet 7.0.102, raspberry pi OS bookworm (debian 12).

Seems like an .net implementation issues for Linux. I can also re-produce on Ubuntu 22.04.

The CvException was thrown in the following process:

  Managed C# code call native C++ code
  => 
  C++ code error, redirected error back to C# Managed code
  =>
  C# throw Exception from the Managed side 
  =>
  Exception can be caught on dotnet in windows, but not in Linux...

The windows dotnet runtime seems to handle this type of Exception throwing correctly, and is able to caught the exception. Linux implementation is not able to propoerly caught that exception and I have got the following error:

Unhandled exception. Emgu.CV.Util.CvException: OpenCV: total >= 0 && (depth == CV_32F || depth == CV_32S)
   at Emgu.CV.CvInvoke.CvErrorHandler(Int32 status, IntPtr funcName, IntPtr errMsg, IntPtr fileName, Int32 line, IntPtr userData) 

Beside of the observation, it looked like the issue need to be handled in the dotnet runtime side.

Any recommendation is welcome.

ghost commented 1 year ago

Apparently, this is by design in the Core CLR. Exceptions thrown in methods called using P/Invoke cannot be caught.

See #35017 and #7342 for example. The user jkotas describes a different mechanism but I doubt that this will be an easy fix.