diff --git a/samples/Sentry.Samples.Console.Customized/Program.cs b/samples/Sentry.Samples.Console.Customized/Program.cs index 7a106f0263..63539e2fd9 100644 --- a/samples/Sentry.Samples.Console.Customized/Program.cs +++ b/samples/Sentry.Samples.Console.Customized/Program.cs @@ -1,6 +1,7 @@ using System; using System.Net.Http; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Sentry; using Sentry.Extensibility; @@ -47,6 +48,9 @@ await SentrySdk.ConfigureScopeAsync(async scope => // Before excluding all prefixed 'LibraryX.', any stack trace from a type namespaced 'LibraryX.Core' will be considered InApp. o.AddInAppInclude("LibraryX.Core"); + // Send events whe unoptimized assemblies are detected + o.NotifyUnoptimizedAssembly = true; + // Send personal identifiable information like the username logged on to the computer and machine name o.SendDefaultPii = true; @@ -108,6 +112,10 @@ await SentrySdk.ConfigureScopeAsync(async scope => SentrySdk.AddBreadcrumb( "A 'bad breadcrumb' that will be rejected because of 'BeforeBreadcrumb callback above.'"); + // Because 'NotifyUnoptimizedAssembly' is enabled, calling this method will raise an event to alert + // that an Assembly that is compiled for Debug was loaded. + LoadUnoptimizedAssembly(); + // Data added to the root scope (no PushScope called up to this point) // The modifications done here will affect all events sent and will propagate to child scopes. await SentrySdk.ConfigureScopeAsync(async scope => @@ -147,7 +155,6 @@ await SentrySdk.ConfigureScopeAsync(async scope => SentrySdk.CaptureException(error); } - var count = 10; for (var i = 0; i < count; i++) { @@ -196,6 +203,13 @@ await SentrySdk.ConfigureScopeAsync(async scope => } // On Dispose: SDK closed, events queued are flushed/sent to Sentry } + [MethodImpl(MethodImplOptions.NoInlining)] + private static void LoadUnoptimizedAssembly() + { + // https://twitter.com/jeremydmiller/status/777571510919630848 + Console.WriteLine(typeof(StructureMap.Container).Assembly.GetName().Version); + } + private class AdminPartMiddleware { private readonly ISentryClient _adminClient; diff --git a/samples/Sentry.Samples.Console.Customized/Sentry.Samples.Console.Customized.csproj b/samples/Sentry.Samples.Console.Customized/Sentry.Samples.Console.Customized.csproj index 70771ec60a..9de0801c9a 100644 --- a/samples/Sentry.Samples.Console.Customized/Sentry.Samples.Console.Customized.csproj +++ b/samples/Sentry.Samples.Console.Customized/Sentry.Samples.Console.Customized.csproj @@ -5,6 +5,8 @@ netcoreapp2.1;net472 false + + false @@ -14,5 +16,8 @@ + + + diff --git a/src/Sentry/Integrations/UnoptimizedAssemblyIntegration.cs b/src/Sentry/Integrations/UnoptimizedAssemblyIntegration.cs new file mode 100644 index 0000000000..831d34a712 --- /dev/null +++ b/src/Sentry/Integrations/UnoptimizedAssemblyIntegration.cs @@ -0,0 +1,68 @@ +using System; +using System.Linq; +using Sentry.Extensibility; +using Sentry.Protocol; +using Sentry.Reflection; + +namespace Sentry.Integrations +{ + /// + /// An integration that emits events when detects assemblies compiled for debug. + /// + public class UnoptimizedAssemblyIntegration : ISdkIntegration + { + /// + /// Registers the integration with the hub and options. + /// + /// The hub to use to send events. + /// The options to configure the integration. + public void Register(IHub hub, SentryOptions options) + { + if (options.NotifyUnoptimizedAssembly) + { + options.DiagnosticLogger?.LogDebug("Subscribing to AssemblyLoad to detect unoptimized assemblies."); + // TODO: .NET Core equivalent + AppDomain.CurrentDomain.AssemblyLoad += (sender, args) => + { + if (!args.LoadedAssembly.IsOptimized()) + { + CaptureEvent(hub, options, args.LoadedAssembly.FullName); + hub.FlushAsync(TimeSpan.FromSeconds(10)).GetAwaiter().GetResult(); + } + }; + + var unoptimizedAssemblies = AppDomain.CurrentDomain.GetAssemblies() + .Where(a => !a.IsOptimized()) + .Select(a => a.FullName) + .ToArray(); + + if (unoptimizedAssemblies.Any()) + { + CaptureEvent(hub, options, unoptimizedAssemblies); + } + } + } + + private static void CaptureEvent(IHub hub, SentryOptions options, params string[] assemblies) + { + if (assemblies.Length > 0) + { + options.DiagnosticLogger?.LogInfo("Unoptimized Assembly found. Raising event for {count} assemblies", assemblies.Length); + using (hub.PushScope()) + { + hub.ConfigureScope(s => + { + s.SetFingerprint(new[] {"UnoptimizedAssemblyIntegration"}); + foreach (var asm in assemblies) + { + options.DiagnosticLogger?.LogInfo("Unoptimized Assembly: {asm}", asm); + s.SetExtra("unoptimized-assembly", asm); + } + s.Level = SentryLevel.Warning; + }); + hub.CaptureMessage("Unoptimized assembly detected."); + } + } + } + } +} diff --git a/src/Sentry/Reflection/AssemblyExtensions.cs b/src/Sentry/Reflection/AssemblyExtensions.cs index 4a9836d0cb..76648ed708 100644 --- a/src/Sentry/Reflection/AssemblyExtensions.cs +++ b/src/Sentry/Reflection/AssemblyExtensions.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.Diagnostics; using System.Reflection; using Sentry.Protocol; @@ -29,5 +30,20 @@ public static SdkVersion GetNameAndVersion(this Assembly asm) return new SdkVersion { Name = name, Version = version }; } + + /// + /// Whether the assembly was compiled with the optimize+ flag + /// + /// The assembly to verify the optimization flag + /// + /// true if no exists or + /// is false, + /// otherwise, false. + /// + public static bool IsOptimized(this Assembly asm) + { + var att = asm.GetCustomAttribute(); + return att == null || att.IsJITOptimizerDisabled == false; + } } } diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 25c28d1d84..1a721f8e14 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -349,6 +349,11 @@ public IDiagnosticLogger DiagnosticLogger /// public bool ReportAssemblies { get; set; } = true; + /// + /// Whether the SDK should raise events if unoptimized assemblies are loaded. + /// + public bool NotifyUnoptimizedAssembly { get; set; } + /// /// What modes to use for event automatic deduplication /// @@ -411,7 +416,8 @@ public SentryOptions() Integrations = ImmutableList.Create( new AppDomainUnhandledExceptionIntegration(), - new AppDomainProcessExitIntegration()); + new AppDomainProcessExitIntegration(), + new UnoptimizedAssemblyIntegration()); InAppExclude = ImmutableList.Create(