Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions fixtures/sdk_crash_detection/crash_event_dotnet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import time
from collections.abc import Mapping, MutableMapping, Sequence


def get_frames(
sdk_frame_module: str, system_frame_module: str
) -> Sequence[MutableMapping[str, str]]:
frames = [
{
"function": "Main",
"module": "System.Threading.ThreadPoolWorkQueue",
"filename": "ThreadPoolWorkQueue.cs",
"abs_path": "ThreadPoolWorkQueue.cs",
},
{
"function": "RunInternal",
"module": "System.Threading.ExecutionContext",
"filename": "ExecutionContext.cs",
"abs_path": "ExecutionContext.cs",
},
{
"function": "MoveNext",
"module": "Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker",
"filename": "ControllerActionInvoker.cs",
"abs_path": "ControllerActionInvoker.cs",
},
{
"function": "PostIndex",
"module": "Samples.AspNetCore.Mvc.Controllers.HomeController",
"filename": "HomeController.cs",
"abs_path": "HomeController.cs",
},
{
"function": "CaptureException",
"module": sdk_frame_module,
"filename": "SentryClient.cs",
"abs_path": "SentryClient.cs",
},
{
"function": "InvokeAsync",
"module": system_frame_module,
"filename": "SentryMiddleware.cs",
},
]
return frames


def get_crash_event(
sdk_frame_module="Sentry.SentryClient",
system_frame_module="System.Runtime.CompilerServices.AsyncTaskMethodBuilder",
**kwargs,
) -> dict[str, object]:
return get_crash_event_with_frames(
get_frames(sdk_frame_module, system_frame_module),
**kwargs,
)


def get_unity_frames(
sdk_frame_module: str, unity_frame_module: str
) -> Sequence[MutableMapping[str, str]]:
frames = [
{
"function": "Update",
"module": "UnityEngine.EventSystems.EventSystem",
"filename": "",
},
{
"function": "OnPointerClick",
"module": "UnityEngine.UI.Button",
"filename": "",
},
{
"function": "Invoke",
"module": "UnityEngine.Events.UnityEvent",
"filename": "",
},
{
"function": "SendMessage",
"module": "SentryTest",
"filename": "",
},
{
"function": "CaptureException",
"module": sdk_frame_module,
"filename": "",
},
{
"function": "Invoke",
"module": unity_frame_module,
"filename": "",
},
]
return frames


def get_unity_crash_event(
sdk_frame_module="Sentry.SentryClient",
unity_frame_module="UnityEngine.Events.InvokableCall",
**kwargs,
) -> dict[str, object]:
return get_crash_event_with_frames(
get_unity_frames(sdk_frame_module, unity_frame_module),
**kwargs,
)


def get_exception(
frames: Sequence[Mapping[str, str]],
mechanism=None,
) -> dict[str, object]:
if mechanism is None:
# linter complains about mutable arguments otherwise
mechanism = {"type": "onerror", "handled": False}
return {
"type": "System.DivideByZeroException",
"value": "Attempted to divide by zero.",
"module": "System",
"stacktrace": {"frames": frames},
"mechanism": mechanism,
}


def get_crash_event_with_frames(frames: Sequence[Mapping[str, str]], **kwargs) -> dict[str, object]:
result = {
"event_id": "0a52a8331d3b45089ebd74f8118d4fa1",
"release": "sentry.dotnet@3.22.0",
"dist": "1",
"platform": "csharp",
"environment": "production",
"exception": {"values": [get_exception(frames)]},
"key_id": "1336851",
"level": "error",
"contexts": {
"device": {
"name": "DESKTOP-ABC123",
"family": "Desktop",
"model": "PC",
"simulator": False,
},
"os": {
"name": "Windows",
"version": "10.0.19041",
"build": "19041.1348",
"kernel_version": "10.0.19041.1348",
"type": "os",
},
"runtime": {
"name": ".NET Core",
"version": "6.0.5",
"type": "runtime",
},
},
"sdk": {"name": "sentry.dotnet", "version": "3.22.0"},
"timestamp": time.time(),
"type": "error",
}

result.update(kwargs)
return result
20 changes: 20 additions & 0 deletions src/sentry/options/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -2548,6 +2548,26 @@
flags=FLAG_AUTOMATOR_MODIFIABLE,
)

register(
"issues.sdk_crash_detection.dotnet.project_id",
default=0,
type=Int,
flags=FLAG_ALLOW_EMPTY | FLAG_AUTOMATOR_MODIFIABLE,
)

register(
"issues.sdk_crash_detection.dotnet.organization_allowlist",
type=Sequence,
default=[],
flags=FLAG_ALLOW_EMPTY | FLAG_AUTOMATOR_MODIFIABLE,
)

register(
"issues.sdk_crash_detection.dotnet.sample_rate",
default=0.0,
flags=FLAG_AUTOMATOR_MODIFIABLE,
)

# END: SDK Crash Detection

register(
Expand Down
67 changes: 67 additions & 0 deletions src/sentry/utils/sdk_crashes/sdk_crash_detection_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class SdkName(Enum):
Java = "java"
Native = "native"
Dart = "dart"
Dotnet = "dotnet"


@dataclass
Expand Down Expand Up @@ -430,6 +431,72 @@ def build_sdk_crash_detection_configs() -> Sequence[SDKCrashDetectionConfig]:
)
configs.append(dart_config)

dotnet_options = _get_options(sdk_name=SdkName.Dotnet, has_organization_allowlist=True)

if dotnet_options:
# Unity SDK contains .NET SDK, so the versions must match. 0.24.0 Unity release was
# based on 3.22.0 .NET release. From that point on SDK names and frames should be consistent.
dotnet_min_sdk_version = "3.22.0"
unity_min_sdk_version = "0.24.0"

dotnet_config = SDKCrashDetectionConfig(
sdk_name=SdkName.Dotnet,
project_id=dotnet_options["project_id"],
sample_rate=dotnet_options["sample_rate"],
organization_allowlist=dotnet_options["organization_allowlist"],
sdk_names={
"sentry.dotnet": dotnet_min_sdk_version,
"sentry.dotnet.android": dotnet_min_sdk_version,
"sentry.dotnet.aspnet": dotnet_min_sdk_version,
"sentry.dotnet.aspnetcore": dotnet_min_sdk_version,
"sentry.dotnet.aspnetcore.grpc": dotnet_min_sdk_version,
"sentry.dotnet.cocoa": dotnet_min_sdk_version,
"sentry.dotnet.ef": dotnet_min_sdk_version,
"sentry.dotnet.extensions.logging": dotnet_min_sdk_version,
"sentry.dotnet.google-cloud-function": dotnet_min_sdk_version,
"sentry.dotnet.log4net": dotnet_min_sdk_version,
"sentry.dotnet.maui": dotnet_min_sdk_version,
"sentry.dotnet.nlog": dotnet_min_sdk_version,
"sentry.dotnet.serilog": dotnet_min_sdk_version,
"sentry.dotnet.xamarin": dotnet_min_sdk_version,
"sentry.dotnet.xamarin-forms": dotnet_min_sdk_version,
"sentry.dotnet.unity": unity_min_sdk_version,
"sentry.unity": unity_min_sdk_version,
"sentry.unity.lite": unity_min_sdk_version,
},
# Report fatal errors, since there are no crashes in Unity
report_fatal_errors=True,
ignore_mechanism_type=set(),
allow_mechanism_type=set(),
system_library_path_patterns={
# .NET System libraries
r"System.**",
r"Microsoft.**",
r"mscorlib**",
r"netstandard**",
# Unity engine libraries
r"UnityEngine.**",
r"UnityEditor.**",
# Common .NET Core/Framework paths
r"**.NETCoreApp**",
r"**.NETFramework**",
r"**.NETStandard**",
},
sdk_frame_config=SDKFrameConfig(
function_patterns=set(),
path_patterns={
# Main Sentry .NET SDK modules
r"Sentry.**",
# Unity-specific Sentry paths (for cases where abs_path is available)
r"**/sentry-unity/**",
r"**/sentry-dotnet/**",
},
path_replacer=KeepFieldPathReplacer(fields={"module", "package", "filename"}),
),
sdk_crash_ignore_matchers=set(),
)
configs.append(dotnet_config)

return configs


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
"issues.sdk_crash_detection.dart.project_id": 5,
"issues.sdk_crash_detection.dart.sample_rate": 0.5,
"issues.sdk_crash_detection.dart.organization_allowlist": [4],
"issues.sdk_crash_detection.dotnet.project_id": 6,
"issues.sdk_crash_detection.dotnet.sample_rate": 0.6,
"issues.sdk_crash_detection.dotnet.organization_allowlist": [5],
}
)
def test_build_sdk_crash_detection_configs() -> None:
configs = build_sdk_crash_detection_configs()

assert len(configs) == 5
assert len(configs) == 6

cocoa_config = configs[0]
assert cocoa_config.sdk_name == SdkName.Cocoa
Expand Down Expand Up @@ -58,6 +61,12 @@ def test_build_sdk_crash_detection_configs() -> None:
assert dart_config.sample_rate == 0.5
assert dart_config.organization_allowlist == [4]

dotnet_config = configs[5]
assert dotnet_config.sdk_name == SdkName.Dotnet
assert dotnet_config.project_id == 6
assert dotnet_config.sample_rate == 0.6
assert dotnet_config.organization_allowlist == [5]


@override_options(
{
Expand All @@ -75,6 +84,9 @@ def test_build_sdk_crash_detection_configs() -> None:
"issues.sdk_crash_detection.dart.project_id": 0,
"issues.sdk_crash_detection.dart.sample_rate": 0.0,
"issues.sdk_crash_detection.dart.organization_allowlist": [],
"issues.sdk_crash_detection.dotnet.project_id": 0,
"issues.sdk_crash_detection.dotnet.sample_rate": 0.0,
"issues.sdk_crash_detection.dotnet.organization_allowlist": [],
}
)
def test_build_sdk_crash_detection_configs_only_react_native() -> None:
Expand Down Expand Up @@ -104,6 +116,9 @@ def test_build_sdk_crash_detection_configs_only_react_native() -> None:
"issues.sdk_crash_detection.dart.project_id": 5,
"issues.sdk_crash_detection.dart.sample_rate": 0.0,
"issues.sdk_crash_detection.dart.organization_allowlist": [4],
"issues.sdk_crash_detection.dotnet.project_id": 6,
"issues.sdk_crash_detection.dotnet.sample_rate": 0.0,
"issues.sdk_crash_detection.dotnet.organization_allowlist": [5],
}
)
def test_build_sdk_crash_detection_configs_no_sample_rate() -> None:
Expand Down Expand Up @@ -133,6 +148,9 @@ def test_build_sdk_crash_detection_configs_no_sample_rate() -> None:
"issues.sdk_crash_detection.dart.project_id": 0,
"issues.sdk_crash_detection.dart.sample_rate": 0.0,
"issues.sdk_crash_detection.dart.organization_allowlist": [],
"issues.sdk_crash_detection.dotnet.project_id": 0,
"issues.sdk_crash_detection.dotnet.sample_rate": 0.0,
"issues.sdk_crash_detection.dotnet.organization_allowlist": [],
}
)
def test_build_sdk_crash_detection_default_configs() -> None:
Expand Down
Loading
Loading