diff --git a/crashlytics/src/AndroidImpl.cs b/crashlytics/src/AndroidImpl.cs index fc65153b..4ed92c00 100644 --- a/crashlytics/src/AndroidImpl.cs +++ b/crashlytics/src/AndroidImpl.cs @@ -20,6 +20,7 @@ namespace Firebase.Crashlytics using System; using System.Diagnostics; using System.Collections.Generic; + using System.Linq; using UnityEngine; @@ -143,6 +144,21 @@ public override void LogException(Exception exception) { public override void LogExceptionAsFatal(Exception exception) { var loggedException = LoggedException.FromException(exception); + Dictionary[] parsedStackTrace = loggedException.ParsedStackTrace; + + if (parsedStackTrace.Length == 0) { + // if for some reason we don't get stack trace from exception, we add current stack trace in + var currentStackTrace = System.Environment.StackTrace; + LoggedException loggedExceptionWithCurrentStackTrace = new LoggedException(loggedException.Name, loggedException.Message, currentStackTrace); + parsedStackTrace = loggedExceptionWithCurrentStackTrace.ParsedStackTrace; + + if (parsedStackTrace.Length > 2) { + // remove RecordCustomException and System.Environment.StackTrace frame for fault blame on crashlytics sdk + var slicedParsedStackTrace = parsedStackTrace.Skip(2).Take(parsedStackTrace.Length - 2).ToArray(); + parsedStackTrace = slicedParsedStackTrace; + } + } + StackFrames frames = new StackFrames(); foreach (Dictionary frame in loggedException.ParsedStackTrace) { frames.Add(new FirebaseCrashlyticsFrame { @@ -152,7 +168,7 @@ public override void LogExceptionAsFatal(Exception exception) { lineNumber = frame["line"], }); } - // TODO(mrober): Do something for empty stacktrace. Get current stacktrace? + CallInternalMethod(() => { crashlyticsInternal.LogExceptionAsFatal(loggedException.Name, loggedException.Message, frames); diff --git a/crashlytics/src/IOSImpl.cs b/crashlytics/src/IOSImpl.cs index 05007d5a..5793fc6b 100644 --- a/crashlytics/src/IOSImpl.cs +++ b/crashlytics/src/IOSImpl.cs @@ -20,6 +20,7 @@ namespace Firebase.Crashlytics using System.Runtime.InteropServices; using System.Diagnostics; using System.Collections.Generic; + using System.Linq; using UnityEngine; @@ -116,6 +117,19 @@ public override void SetCrashlyticsCollectionEnabled(bool enabled) { private void RecordCustomException(LoggedException loggedException, bool isOnDemand) { Dictionary[] parsedStackTrace = loggedException.ParsedStackTrace; + if (isOnDemand && parsedStackTrace.Length == 0) { + // if for some reason we don't get stack trace from exception, we add current stack trace in + var currentStackTrace = System.Environment.StackTrace; + LoggedException loggedExceptionWithCurrentStackTrace = new LoggedException(loggedException.Name, loggedException.Message, currentStackTrace); + parsedStackTrace = loggedExceptionWithCurrentStackTrace.ParsedStackTrace; + + if (parsedStackTrace.Length > 2) { + // remove RecordCustomException and System.Environment.StackTrace frame for fault blame on crashlytics sdk + var slicedParsedStackTrace = parsedStackTrace.Skip(2).Take(parsedStackTrace.Length - 2).ToArray(); + parsedStackTrace = slicedParsedStackTrace; + } + } + List frames = new List(); foreach (Dictionary frame in parsedStackTrace) { frames.Add(new Frame { diff --git a/crashlytics/src/cpp/ios/Crashlytics_PrivateHeaders/ExceptionModel_Platform.h b/crashlytics/src/cpp/ios/Crashlytics_PrivateHeaders/ExceptionModel_Platform.h new file mode 100644 index 00000000..605b0db5 --- /dev/null +++ b/crashlytics/src/cpp/ios/Crashlytics_PrivateHeaders/ExceptionModel_Platform.h @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// Crashlytics_ExceptionModel.h +// Crashlytics +// + +#import + +@interface FIRExceptionModel (Platform) + +@property(nonatomic) BOOL isFatal; +@property(nonatomic) BOOL onDemand; + +@end \ No newline at end of file diff --git a/crashlytics/src/cpp/ios/CrashlyticsiOSWrapper.m b/crashlytics/src/cpp/ios/CrashlyticsiOSWrapper.m index ff5ca2f3..4ad645d1 100644 --- a/crashlytics/src/cpp/ios/CrashlyticsiOSWrapper.m +++ b/crashlytics/src/cpp/ios/CrashlyticsiOSWrapper.m @@ -21,6 +21,7 @@ #import "CrashlyticsiOSWrapper.h" #import "FirebaseCrashlytics.h" #import "./Crashlytics_PrivateHeaders/Crashlytics_Platform.h" +#import "./Crashlytics_PrivateHeaders/ExceptionModel_Platform.h" @interface FIRCrashlytics () - (BOOL)isCrashlyticsStarted; @@ -58,6 +59,9 @@ void CLURecordCustomException(const char *name, const char *reason, Frame *frame model.stackTrace = framesArray; if (isOnDemand) { + // For on demand exception, we log them as fatal + model.onDemand = YES; + model.isFatal = YES; [[FIRCrashlytics crashlytics] recordOnDemandExceptionModel:model]; return; }