Skip to content

Commit

Permalink
Fix invalid assertion issues grouping (#537)
Browse files Browse the repository at this point in the history
  • Loading branch information
tustanivsky authored Jun 10, 2024
1 parent 1fbef40 commit 51bea9b
Show file tree
Hide file tree
Showing 23 changed files with 331 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

### Fixes

- The SDK now correctly captures and groups Assertions ([#537](https://github.com/getsentry/sentry-unreal/pull/537))
- Add path strings escaping for debug symbol upload script ([#561](https://github.com/getsentry/sentry-unreal/pull/561))
- Fix crashes not being reported during garbage collection ([#566](https://github.com/getsentry/sentry-unreal/pull/566))
- The SDK now uploads debug symbols properly with the `Android File Server` plugin enabled in UE 5.0 and newer ([#568](https://github.com/getsentry/sentry-unreal/pull/568))
Expand Down
53 changes: 43 additions & 10 deletions plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
package io.sentry.unreal;

import android.app.Activity;
import android.util.Log;

import androidx.annotation.NonNull;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.sentry.Breadcrumb;
Expand All @@ -26,7 +27,10 @@
import io.sentry.SentryOptions;
import io.sentry.android.core.SentryAndroid;
import io.sentry.android.core.SentryAndroidOptions;
import io.sentry.protocol.SentryException;
import io.sentry.protocol.SentryId;
import io.sentry.protocol.SentryStackFrame;
import io.sentry.protocol.SentryStackTrace;

public class SentryBridgeJava {
public static native void onConfigureScope(long callbackAddr, IScope scope);
Expand All @@ -53,6 +57,7 @@ public void configure(SentryAndroidOptions options) {
options.setBeforeSend(new SentryOptions.BeforeSendCallback() {
@Override
public SentryEvent execute(SentryEvent event, Hint hint) {
preProcessEvent(event);
return onBeforeSend(beforeSendHandler, event, hint);
}
});
Expand All @@ -73,12 +78,12 @@ public SentryEvent execute(SentryEvent event, Hint hint) {
options.setTracesSampler(new SentryOptions.TracesSamplerCallback() {
@Override
public Double sample(SamplingContext samplingContext) {
float sampleRate = onTracesSampler(samplerAddr, samplingContext);
if(sampleRate >= 0.0f) {
return (double) sampleRate;
} else {
return null;
}
float sampleRate = onTracesSampler(samplerAddr, samplingContext);
if(sampleRate >= 0.0f) {
return (double) sampleRate;
} else {
return null;
}
}
});
}
Expand All @@ -89,6 +94,24 @@ public Double sample(SamplingContext samplingContext) {
});
}

private static void preProcessEvent(SentryEvent event) {
if (event.getTags().containsKey("sentry_unreal_exception")) {
SentryException exception = event.getUnhandledException();
if (exception != null) {
exception.setType(event.getTag("sentry_unreal_exception_type"));
exception.setValue(event.getTag("sentry_unreal_exception_message"));
SentryStackTrace trace = exception.getStacktrace();
int numFramesToSkip = Integer.parseInt(event.getTag("sentry_unreal_exception_skip_frames"));
List<SentryStackFrame> frames = trace.getFrames();
trace.setFrames(frames.subList(0, frames.size() - numFramesToSkip));
}
event.removeTag("sentry_unreal_exception_type");
event.removeTag("sentry_unreal_exception_message");
event.removeTag("sentry_unreal_exception_skip_frames");
event.removeTag("sentry_unreal_exception");
}
}

public static void addBreadcrumb(final String message, final String category, final String type, final HashMap<String, String> data, final SentryLevel level) {
Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setMessage(message);
Expand All @@ -106,8 +129,8 @@ public static SentryId captureMessageWithScope(final String message, final Sentr
SentryId messageId = Sentry.captureMessage(message, new ScopeCallback() {
@Override
public void run(@NonNull IScope scope) {
scope.setLevel(level);
onConfigureScope(callback, scope);
scope.setLevel(level);
onConfigureScope(callback, scope);
}
});
return messageId;
Expand All @@ -117,12 +140,22 @@ public static SentryId captureEventWithScope(final SentryEvent event, final long
SentryId eventId = Sentry.captureEvent(event, new ScopeCallback() {
@Override
public void run(@NonNull IScope scope) {
onConfigureScope(callback, scope);
onConfigureScope(callback, scope);
}
});
return eventId;
}

public static SentryId captureException(final String type, final String value) {
SentryException exception = new SentryException();
exception.setType(type);
exception.setValue(value);
SentryEvent event = new SentryEvent();
event.setExceptions(Collections.singletonList(exception));
SentryId eventId = Sentry.captureEvent(event);
return eventId;
}

public static void configureScope(final long callback) {
Sentry.configureScope(new ScopeCallback() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,34 @@ USentryId* SentrySubsystemAndroid::CaptureEventWithScope(USentryEvent* event, co
return SentryConvertorsAndroid::SentryIdToUnreal(*id);
}

USentryId* SentrySubsystemAndroid::CaptureException(const FString& type, const FString& message, int32 framesToSkip)
{
return nullptr;
}

USentryId* SentrySubsystemAndroid::CaptureAssertion(const FString& type, const FString& message)
{
const int32 framesToSkip = 8;

// add marker tags specific for Unreal assertions
SetTag(TEXT("sentry_unreal_exception"), TEXT("assert"));
SetTag(TEXT("sentry_unreal_exception_skip_frames"), FString::Printf(TEXT("%d"), framesToSkip));
SetTag(TEXT("sentry_unreal_exception_type"), type);
SetTag(TEXT("sentry_unreal_exception_message"), message);

PLATFORM_BREAK();

return nullptr;
}

USentryId* SentrySubsystemAndroid::CaptureEnsure(const FString& type, const FString& message)
{
auto id = FSentryJavaObjectWrapper::CallStaticObjectMethod<jobject>(SentryJavaClasses::SentryBridgeJava, "captureException", "(Ljava/lang/String;Ljava/lang/String;)Lio/sentry/protocol/SentryId;",
*FSentryJavaObjectWrapper::GetJString(type), *FSentryJavaObjectWrapper::GetJString(message));

return SentryConvertorsAndroid::SentryIdToUnreal(*id);
}

void SentrySubsystemAndroid::CaptureUserFeedback(USentryUserFeedback* userFeedback)
{
TSharedPtr<SentryUserFeedbackAndroid> userFeedbackAndroid = StaticCastSharedPtr<SentryUserFeedbackAndroid>(userFeedback->GetNativeImpl());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class SentrySubsystemAndroid : public ISentrySubsystem
virtual USentryId* CaptureMessageWithScope(const FString& message, const FConfigureScopeNativeDelegate& onConfigureScope, ESentryLevel level) override;
virtual USentryId* CaptureEvent(USentryEvent* event) override;
virtual USentryId* CaptureEventWithScope(USentryEvent* event, const FConfigureScopeNativeDelegate& onConfigureScope) override;
virtual USentryId* CaptureException(const FString& type, const FString& message, int32 framesToSkip) override;
virtual USentryId* CaptureAssertion(const FString& type, const FString& message) override;
virtual USentryId* CaptureEnsure(const FString& type, const FString& message) override;
virtual void CaptureUserFeedback(USentryUserFeedback* userFeedback) override;
virtual void SetUser(USentryUser* user) override;
virtual void RemoveUser() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "Apple/SentryTransactionApple.h"
#include "Apple/SentrySpanApple.h"

#include "Convenience/SentryMacro.h"

SentryLevel SentryConvertorsApple::SentryLevelToNative(ESentryLevel level)
{
SentryLevel nativeLevel = kSentryLevelDebug;
Expand Down Expand Up @@ -72,6 +74,24 @@ NSData* SentryConvertorsApple::ByteDataToNative(const TArray<uint8>& array)
return [NSData dataWithBytes:array.GetData() length:array.Num()];
}

SentryStacktrace* SentryConvertorsApple::CallstackToNative(const TArray<FProgramCounterSymbolInfo>& callstack)
{
int32 framesCount = callstack.Num();

NSMutableArray *arr = [NSMutableArray arrayWithCapacity:framesCount];

for (int i = 0; i < framesCount; ++i)
{
SentryFrame *frame = [[SENTRY_APPLE_CLASS(SentryFrame) alloc] init];
frame.instructionAddress = FString::Printf(TEXT("0x%llx"), callstack[framesCount - i - 1].ProgramCounter).GetNSString();
[arr addObject:frame];
}

SentryStacktrace *trace = [[SENTRY_APPLE_CLASS(SentryStacktrace) alloc] initWithFrames:arr registers:@{}];

return trace;
}

ESentryLevel SentryConvertorsApple::SentryLevelToUnreal(SentryLevel level)
{
ESentryLevel unrealLevel = ESentryLevel::Debug;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include "Convenience/SentryInclude.h"

#include "GenericPlatform/GenericPlatformStackWalk.h"

class USentryTransactionContext;
class USentryScope;
class USentryId;
Expand All @@ -20,6 +22,7 @@ class SentryConvertorsApple
static NSDictionary* StringMapToNative(const TMap<FString, FString>& map);
static NSArray* StringArrayToNative(const TArray<FString>& array);
static NSData* ByteDataToNative(const TArray<uint8>& array);
static SentryStacktrace* CallstackToNative(const TArray<FProgramCounterSymbolInfo>& callstack);

/** Conversions from native iOS types */
static ESentryLevel SentryLevelToUnreal(SentryLevel level);
Expand Down
31 changes: 31 additions & 0 deletions plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,37 @@ USentryId* SentrySubsystemApple::CaptureEventWithScope(USentryEvent* event, cons
return SentryConvertorsApple::SentryIdToUnreal(id);
}

USentryId* SentrySubsystemApple::CaptureException(const FString& type, const FString& message, int32 framesToSkip)
{
auto StackFrames = FGenericPlatformStackWalk::GetStack(framesToSkip);

SentryException *nativeException = [[SENTRY_APPLE_CLASS(SentryException) alloc] initWithValue:message.GetNSString() type:type.GetNSString()];
NSMutableArray *nativeExceptionArray = [NSMutableArray arrayWithCapacity:1];
[nativeExceptionArray addObject:nativeException];

SentryEvent *exceptionEvent = [[SENTRY_APPLE_CLASS(SentryEvent) alloc] init];
exceptionEvent.exceptions = nativeExceptionArray;
exceptionEvent.stacktrace = SentryConvertorsApple::CallstackToNative(StackFrames);

SentryId* id = [SENTRY_APPLE_CLASS(SentrySDK) captureEvent:exceptionEvent];
return SentryConvertorsApple::SentryIdToUnreal(id);
}

USentryId* SentrySubsystemApple::CaptureAssertion(const FString& type, const FString& message)
{
#if PLATFORM_MAC
int32 framesToSkip = 6;
#elif PLATFORM_IOS
int32 framesToSkip = 5;
#endif
return CaptureException(type, message, framesToSkip);
}

USentryId* SentrySubsystemApple::CaptureEnsure(const FString& type, const FString& message)
{
return CaptureException(type, message, 6);
}

void SentrySubsystemApple::CaptureUserFeedback(USentryUserFeedback* userFeedback)
{
TSharedPtr<SentryUserFeedbackApple> userFeedbackIOS = StaticCastSharedPtr<SentryUserFeedbackApple>(userFeedback->GetNativeImpl());
Expand Down
3 changes: 3 additions & 0 deletions plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class SentrySubsystemApple : public ISentrySubsystem
virtual USentryId* CaptureMessageWithScope(const FString& message, const FConfigureScopeNativeDelegate& onConfigureScope, ESentryLevel level) override;
virtual USentryId* CaptureEvent(USentryEvent* event) override;
virtual USentryId* CaptureEventWithScope(USentryEvent* event, const FConfigureScopeNativeDelegate& onConfigureScope) override;
virtual USentryId* CaptureException(const FString& type, const FString& message, int32 framesToSkip) override;
virtual USentryId* CaptureAssertion(const FString& type, const FString& message) override;
virtual USentryId* CaptureEnsure(const FString& type, const FString& message) override;
virtual void CaptureUserFeedback(USentryUserFeedback* userFeedback) override;
virtual void SetUser(USentryUser* user) override;
virtual void RemoveUser() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,36 @@ sentry_value_t SentryConvertorsDesktop::StringArrayToNative(const TArray<FString
return sentryArray;
}

sentry_value_t SentryConvertorsDesktop::AddressToNative(uint64 address)
{
char buffer[32];
size_t written = (size_t)snprintf(buffer, sizeof(buffer), "0x%llx", (unsigned long long)address);
if (written >= sizeof(buffer))
{
return sentry_value_new_null();
}
buffer[written] = '\0';
return sentry_value_new_string(buffer);
}

sentry_value_t SentryConvertorsDesktop::CallstackToNative(const TArray<FProgramCounterSymbolInfo>& callstack)
{
int32 framesCount = callstack.Num();

sentry_value_t frames = sentry_value_new_list();
for (int i = 0; i < framesCount; ++i)
{
sentry_value_t frame = sentry_value_new_object();
sentry_value_set_by_key(frame, "instruction_addr", AddressToNative(callstack[framesCount - i - 1].ProgramCounter));
sentry_value_append(frames, frame);
}

sentry_value_t stacktrace = sentry_value_new_object();
sentry_value_set_by_key(stacktrace, "frames", frames);

return stacktrace;
}

ESentryLevel SentryConvertorsDesktop::SentryLevelToUnreal(sentry_value_t level)
{
FString levelStr = FString(sentry_value_as_string(level));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include "Convenience/SentryInclude.h"

#include "GenericPlatform/GenericPlatformStackWalk.h"

#if USE_SENTRY_NATIVE

class USentryId;
Expand All @@ -18,7 +20,9 @@ class SentryConvertorsDesktop
/** Conversions to native desktop (Windows/Mac) types */
static sentry_level_e SentryLevelToNative(ESentryLevel level);
static sentry_value_t StringMapToNative(const TMap<FString, FString>& map);
static sentry_value_t StringArrayToNative(const TArray<FString>& array );
static sentry_value_t StringArrayToNative(const TArray<FString>& array);
static sentry_value_t AddressToNative(uint64 address);
static sentry_value_t CallstackToNative(const TArray<FProgramCounterSymbolInfo>& callstack);

/** Conversions from native desktop (Windows/Mac) types */
static ESentryLevel SentryLevelToUnreal(sentry_value_t level);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ void SentrySubsystemDesktop::InitWithSettings(const USentrySettings* settings, U
sentry_options_set_max_breadcrumbs(options, settings->MaxBreadcrumbs);
sentry_options_set_before_send(options, HandleBeforeSend, this);
sentry_options_set_on_crash(options, HandleBeforeCrash, this);
sentry_options_set_shutdown_timeout(options, 3000);

#if PLATFORM_LINUX
sentry_options_set_transport(options, FSentryTransport::Create());
Expand Down Expand Up @@ -326,6 +327,35 @@ USentryId* SentrySubsystemDesktop::CaptureEventWithScope(USentryEvent* event, co
return Id;
}

USentryId* SentrySubsystemDesktop::CaptureException(const FString& type, const FString& message, int32 framesToSkip)
{
sentry_value_t exceptionEvent = sentry_value_new_event();

auto StackFrames = FGenericPlatformStackWalk::GetStack(framesToSkip);
sentry_value_set_by_key(exceptionEvent, "stacktrace", SentryConvertorsDesktop::CallstackToNative(StackFrames));

sentry_value_t nativeException = sentry_value_new_exception(TCHAR_TO_ANSI(*type), TCHAR_TO_ANSI(*message));
sentry_event_add_exception(exceptionEvent, nativeException);

sentry_uuid_t id = sentry_capture_event(exceptionEvent);
return SentryConvertorsDesktop::SentryIdToUnreal(id);
}

USentryId* SentrySubsystemDesktop::CaptureAssertion(const FString& type, const FString& message)
{
return CaptureException(type, message, 7);
}

USentryId* SentrySubsystemDesktop::CaptureEnsure(const FString& type, const FString& message)
{
#if PLATFORM_WINDOWS && ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3
int32 framesToSkip = 8;
#else
int32 framesToSkip = 7;
#endif
return CaptureException(type, message, framesToSkip);
}

void SentrySubsystemDesktop::CaptureUserFeedback(USentryUserFeedback* userFeedback)
{
TSharedPtr<SentryUserFeedbackDesktop> userFeedbackDesktop = StaticCastSharedPtr<SentryUserFeedbackDesktop>(userFeedback->GetNativeImpl());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class SentrySubsystemDesktop : public ISentrySubsystem
virtual USentryId* CaptureMessageWithScope(const FString& message, const FConfigureScopeNativeDelegate& onScopeConfigure, ESentryLevel level) override;
virtual USentryId* CaptureEvent(USentryEvent* event) override;
virtual USentryId* CaptureEventWithScope(USentryEvent* event, const FConfigureScopeNativeDelegate& onScopeConfigure) override;
virtual USentryId* CaptureException(const FString& type, const FString& message, int32 framesToSkip) override;
virtual USentryId* CaptureAssertion(const FString& type, const FString& message) override;
virtual USentryId* CaptureEnsure(const FString& type, const FString& message) override;
virtual void CaptureUserFeedback(USentryUserFeedback* userFeedback) override;
virtual void SetUser(USentryUser* user) override;
virtual void RemoveUser() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class ISentrySubsystem
virtual USentryId* CaptureMessageWithScope(const FString& message, const FConfigureScopeNativeDelegate& onConfigureScope, ESentryLevel level) = 0;
virtual USentryId* CaptureEvent(USentryEvent* event) = 0;
virtual USentryId* CaptureEventWithScope(USentryEvent* event, const FConfigureScopeNativeDelegate& onConfigureScope) = 0;
virtual USentryId* CaptureException(const FString& type, const FString& message, int32 framesToSkip = 0) = 0;
virtual USentryId* CaptureAssertion(const FString& type, const FString& message) = 0;
virtual USentryId* CaptureEnsure(const FString& type, const FString& message) = 0;
virtual void CaptureUserFeedback(USentryUserFeedback* userFeedback) = 0;
virtual void SetUser(USentryUser* user) = 0;
virtual void RemoveUser() = 0;
Expand Down
Loading

0 comments on commit 51bea9b

Please sign in to comment.