Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw NotSupportedException when using SetErrorStatusOnException in Mono Runtime and Native AOT environment. #5374

Merged
merged 23 commits into from
Mar 2, 2024

Conversation

Yun-Ting
Copy link
Contributor

@Yun-Ting Yun-Ting commented Feb 21, 2024

Mitigates #5358
Design discussion issue #

Changes

Throw NotSupportedException when using ExceptionProcessor in Mono Runtime and Native AOT environment.

Merge requirement checklist

  • CONTRIBUTING guidelines followed (license requirements, nullable enabled, static analysis, etc.)
  • Unit tests added/updated
  • Appropriate CHANGELOG.md files updated for non-trivial changes
  • Changes in public API reviewed (if applicable)

@Yun-Ting Yun-Ting marked this pull request as ready for review February 21, 2024 00:28
@Yun-Ting Yun-Ting requested a review from a team February 21, 2024 00:28
this.fnGetExceptionPointers = Marshal.GetExceptionPointers;
if (RuntimeFeature.IsDynamicCodeSupported)
{
throw new NotSupportedException($"'{typeof(Marshal).FullName}.GetExceptionPointers' is not supported when running native AOT.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to make this a build time error? I think that will help the developers to discover the issue earlier.

@MichalStrehovsky
Copy link

If throwing is the only possible course of action, maybe this should be "fixed" on the .NET runtime side.

RuntimeFeature.IsDynamicCodeSupported is not great for approximating this (as discussed in #5358) because we might have a configuration in the future that responds false to this while still supporting this API.

The only reason to add this check here is that it will consistently throw between F5 debugging (where we emulate IsDynamicCodeSupported == false even though the runtime supports GetExceptionPointers), and publish (that doesn't support GetExceptionPointers, and also responds false to IsDynamicCodeSupported).

I wonder if we should instead "break" this API during F5 debugging instead - same way we break Reflection.Emit or COM interop when PublishAot is set in the project.

Then OpenTelemetry doesn't need to overapproximate the conditions when GetExceptionPointers won't work.

@eerhardt @AaronRobinsonMSFT @jkotas @agocke any opinion?

@eerhardt
Copy link
Contributor

I agree that it is a better experience to get an exception during F5 development when PublishAot is set in the project. So I'd be in favor of throwing from the .NET runtime if possible.

For my education, what would it take to actually support the API in native AOT? Or provide some other API that would allow opentelemetry to work on all platforms. (See #1853 and #1858 for the scenario that this code is trying to enable.)

@@ -19,7 +21,14 @@ internal sealed class ExceptionProcessor : BaseProcessor<Activity>
public ExceptionProcessor()
{
#if NET6_0_OR_GREATER || NETFRAMEWORK
this.fnGetExceptionPointers = Marshal.GetExceptionPointers;
if (RuntimeFeature.IsDynamicCodeSupported)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (RuntimeFeature.IsDynamicCodeSupported)
bool getExceptionPointersSupported = true;
try { Marshal.GetExceptionPointers(); } catch { getExceptionPointersSupported = false; }
if (!getExceptionPointersSupported)
throw new throw new NotSupportedException($"'{typeof(Marshal).FullName}.GetExceptionPointers' is not supported.");

Instead of calling RuntimeFeature.IsDynamicCodeSupported, can this just call the API to see whether it is supported?

Note that this API is also not supported in Mono (irrespective of whether IsDynamicCodeSupported is true or false), so it would cover the Mono.

Copy link
Contributor Author

@Yun-Ting Yun-Ting Feb 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what would it take to actually support the API in native AOT?

Yes, it should be possible to simulated the API in native AOT. The API would need to allocate a piece of memory with the same lifetime as the current exception in flight (if it was not previously allocated), fill it with gunk, and return the pointer to it. The "new" exception handling that is being enabled for CoreCLR and that is not internally based on SEH proves that it is possible.

(Also, as I have said earlier - introducing a new API that is explicitly designed for this scenario would be the proper solution.)

Thank you! I agree above is the proper way to handle the situation. Shall I log this feature request in the runtime repo?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall I log this feature request in the runtime repo?

Yes, please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've filed a request to implement the API in runtime: dotnet/runtime#98878.
Chatted with @eerhardt, @jkotas and @reyang offline.

While the above API is being implemented, we may do the followings to avoid crashing the consumer's app:

  1. Annotate SetErrorStatusOnException() with [RequiresDynamicCode] to serve as a compile time warning for the consumer. One caveat of this approach is IsDynamicCode does not really explain the nature of this issue and may further confuse the consumer.
  2. Use try/catch to swallow the exception if users hit System.PlatformNotSupportedException upon calling Marshal.GetExceptionPointer(). Store the state in ExceptionProcessor() and dump the Message "Exception processor not supported" inside OnEnd(Activity activity).

Not sure which one is a better stopgap of this issue?
cc @MichalStrehovsky, @agocke

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed in today's SIG meeting.
I'll go with adding the compile time warning as was mentioned in 1. above.
Plus, guarding the extension method

public TracerProviderBuilder SetErrorStatusOnException(bool enabled)

with a try/catch on Marshal.GetExceptionPointers to avoid adding useless Processor at runtime (if user ever decided to suppress the compile time AOT warning.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to add try/catch block. We just have to call Marshal.GetExceptionPointers() once in the ExceptionProcessor ctor and rethrow the exception with our custom message.

@CodeBlanch Just to confirm this is what you suggested, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with either re-throwing or just silently failing & not adding the processor. Probably re-throwing is better to alert the users 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on re-throwing the exception at start-up.

@jkotas
Copy link

jkotas commented Feb 21, 2024

if we should instead "break" this API during F5 debugging instead - same way we break Reflection.Emit or COM interop when PublishAot is set in the project.

I do not think that it is worth it. OpenTelemetry is not using for what the API is meant for. This API exists to support _exception_info C++ compiler intrinsic that is part of SEH.

Then OpenTelemetry doesn't need to overapproximate the conditions when GetExceptionPointers won't work.

I expect that OpenTelemetry would still want to do something to avoid crashing, so this would not help. Check my suggestion https://github.com/open-telemetry/opentelemetry-dotnet/pull/5374/files#r1497791686

what would it take to actually support the API in native AOT?

Yes, it should be possible to simulated the API in native AOT. The API would need to allocate a piece of memory with the same lifetime as the current exception in flight (if it was not previously allocated), fill it with gunk, and return the pointer to it. The "new" exception handling that is being enabled for CoreCLR and that is not internally based on SEH proves that it is possible.

(Also, as I have said earlier - introducing a new API that is explicitly designed for this scenario would be the proper solution.)

@jkotas
Copy link

jkotas commented Feb 21, 2024

GetExceptionPointers() seems to be used as a unique identifier of the exception in flight here. GetExceptionPointers() is not a unique identifier of the exception in flight. Sequence of two thrown and caught exceptions can have the same pointer returned from GetExceptionPointers() if (snapshot != pointers) check does not look reliable to me. It may be another reason to introduce a new API for this scenario.

Copy link

codecov bot commented Feb 29, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 84.52%. Comparing base (6250307) to head (8db5d36).
Report is 109 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #5374      +/-   ##
==========================================
+ Coverage   83.38%   84.52%   +1.14%     
==========================================
  Files         297      284      -13     
  Lines       12531    12105     -426     
==========================================
- Hits        10449    10232     -217     
+ Misses       2082     1873     -209     
Flag Coverage Δ
unittests ?
unittests-Solution-Experimental 84.51% <100.00%> (?)
unittests-Solution-Stable 84.50% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
...y/Trace/Builder/TracerProviderBuilderExtensions.cs 100.00% <ø> (ø)
src/OpenTelemetry/Trace/ExceptionProcessor.cs 100.00% <100.00%> (ø)

... and 57 files with indirect coverage changes

@Yun-Ting Yun-Ting changed the title Throw NotSupportedException when using ExceptionProcessor in native AOT. Throw NotSupportedException when using ExceptionProcessor in Mono Runtime and Native AOT enviornment. Feb 29, 2024
@Yun-Ting Yun-Ting changed the title Throw NotSupportedException when using ExceptionProcessor in Mono Runtime and Native AOT enviornment. Throw NotSupportedException when using SetErrorStatusOnException in Mono Runtime and Native AOT environment. Feb 29, 2024
@utpilla utpilla merged commit 28ead76 into open-telemetry:main Mar 2, 2024
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants