Skip to content

Commit

Permalink
feat: cocoa debug image info
Browse files Browse the repository at this point in the history
  • Loading branch information
vaind committed Oct 24, 2023
1 parent 8d014ed commit 1dc256f
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 28 deletions.
12 changes: 10 additions & 2 deletions src/Sentry/Internal/DebugStackTrace.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Sentry.Internal.Extensions;
using Sentry.Extensibility;
using Sentry.Internal.ILSpy;
#if SENTRY_NATIVE
using Sentry.Native;
#endif

namespace Sentry.Internal;

Expand Down Expand Up @@ -229,7 +231,7 @@ private IEnumerable<SentryStackFrame> CreateFrames(StackTrace stackTrace, bool i

/// <summary>
/// Native AOT implementation of CreateFrame.
/// Native frames have only limited method information at runtime (and even that can be disabled).
/// Native frames have only limited method information at runtime (and even that can be disabled).
/// We try to parse that and also add addresses for server-side symbolication.
/// </summary>
private SentryStackFrame? TryCreateNativeAOTFrame(IStackFrame stackFrame)
Expand All @@ -244,7 +246,13 @@ private IEnumerable<SentryStackFrame> CreateFrames(StackTrace stackTrace, bool i
frame.ImageAddress = imageAddress;
frame.InstructionAddress = stackFrame.GetNativeIP();

#if SENTRY_NATIVE
_nativeDebugImages ??= C.LoadDebugImages(_options.DiagnosticLogger);
#elif MACOS
_nativeDebugImages ??= SentrySdk.LoadDebugImages(_options.DiagnosticLogger);
#else
_nativeDebugImages ??= new();
#endif
if (!_usedNativeDebugImages.Contains(imageAddress) && _nativeDebugImages.TryGetValue(imageAddress, out var debugImage))
{
_usedNativeDebugImages.Add(imageAddress);
Expand All @@ -255,7 +263,7 @@ private IEnumerable<SentryStackFrame> CreateFrames(StackTrace stackTrace, bool i
}

// Method info is currently only exposed by ToString(), see https://github.com/dotnet/runtime/issues/92869
// We only care about the case where the method is available (`StackTraceSupport` property is the default `true`):
// We only care about the case where the method is available (`StackTraceSupport` property is the default `true`):
// https://github.com/dotnet/runtime/blob/254230253da143a082f47cfaf8711627c0bf2faf/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs#L42
internal static SentryStackFrame ParseNativeAOTToString(string info)
{
Expand Down
15 changes: 15 additions & 0 deletions src/Sentry/Internal/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,19 @@ public static string ToSnakeCase(this string str) =>
/// Otherwise, returns <paramref name="str"/>.
/// </summary>
public static string? NullIfWhitespace(this string? str) => string.IsNullOrWhiteSpace(str) ? null : str;

public static long ParseHexAsLong(this string str)
{
// It should be in hex format, such as "0x7fff5bf346c0"
if (str.StartsWith("0x") &&
long.TryParse(str[2..], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
{
return result;
}
else if (long.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out result))
{
return result;
}
throw new FormatException($"ParseHexAsLong() cannot parse '{str}'");
}
}
26 changes: 5 additions & 21 deletions src/Sentry/Platforms/Native/CFunctions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Sentry.Extensibility;
using Sentry.Internal.Extensions;

namespace Sentry.Native;

Expand Down Expand Up @@ -44,25 +45,6 @@ internal static void SetValueIfNotNull(sentry_value_t obj, string key, double? v
return sentry_value_is_null(cValue) == 0 ? cValue : null;
}

internal static long? GetValueHex(sentry_value_t obj, string key)
{
if (GetValueString(obj, key) is { } s && s.Length > 0)
{
// It should be in hex format, such as "0x7fff5bf346c0"
if (s.StartsWith("0x") &&
long.TryParse(s[2..], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
{
return result;
}
else if (long.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out result))
{
return result;
}
throw new FormatException($"GetValueHex() cannot parse '{s}'");
}
return null;
}

internal static string? GetValueString(sentry_value_t obj, string key)
{
if (GetValueOrNul(obj, key) is { } cValue)
Expand Down Expand Up @@ -167,6 +149,7 @@ internal static void SetValueIfNotNull(sentry_value_t obj, string key, double? v

internal static Dictionary<long, DebugImage> LoadDebugImages(IDiagnosticLogger? logger)
{
logger?.LogDebug("Collecting a list of native debug images.");
var result = new Dictionary<long, DebugImage>();
try
{
Expand All @@ -176,6 +159,7 @@ internal static Dictionary<long, DebugImage> LoadDebugImages(IDiagnosticLogger?
if (!IsNull(cList))
{
var len = sentry_value_get_length(cList).ToUInt32();
logger?.LogDebug("There are {0} native debug images, parsing the information.", len);
for (uint i = 0; i < len; i++)
{
var cItem = sentry_value_get_by_index(cList, (UIntPtr)i);
Expand All @@ -185,8 +169,9 @@ internal static Dictionary<long, DebugImage> LoadDebugImages(IDiagnosticLogger?
// * https://github.com/getsentry/sentry-native/blob/8faa78298da68d68043f0c3bd694f756c0e95dfa/src/modulefinder/sentry_modulefinder_windows.c#L81
// * https://github.com/getsentry/sentry-native/blob/8faa78298da68d68043f0c3bd694f756c0e95dfa/src/modulefinder/sentry_modulefinder_windows.c#L24
// * https://github.com/getsentry/sentry-native/blob/c5c31e56d36bed37fa5422750a591f44502edb41/src/modulefinder/sentry_modulefinder_linux.c#L465
if (GetValueHex(cItem, "image_addr") is { } imageAddress)
if (GetValueString(cItem, "image_addr") is { } imageAddr && imageAddr.Length > 0)
{
var imageAddress = imageAddr.ParseHexAsLong();
result.Add(imageAddress, new DebugImage()
{
CodeFile = GetValueString(cItem, "code_file"),
Expand All @@ -209,7 +194,6 @@ internal static Dictionary<long, DebugImage> LoadDebugImages(IDiagnosticLogger?
}
catch (Exception e)
{
// Adding the Sentry logger tag ensures we don't send this error to Sentry.
logger?.LogWarning("Error loading the list of debug images", e);
}
return result;
Expand Down
3 changes: 3 additions & 0 deletions src/Sentry/Platforms/Native/Sentry.Native.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
<ItemGroup>
<ProjectReference Include="..\Sentry.Bindings.Native\Sentry.Bindings.Native.csproj" />
</ItemGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);SENTRY_NATIVE</DefineConstants>
</PropertyGroup>
</Project>
43 changes: 38 additions & 5 deletions src/Sentry/Platforms/iOS/SentrySdk.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Sentry.iOS;
using Sentry.iOS.Extensions;
using Sentry.Extensibility;
using Sentry.Internal.Extensions;

// ReSharper disable once CheckNamespace
namespace Sentry;
Expand Down Expand Up @@ -27,14 +29,14 @@ private static void InitSentryCocoaSdk(SentryOptions options)
cocoaOptions.DiagnosticLevel = options.DiagnosticLevel.ToCocoaSentryLevel();
cocoaOptions.Dsn = options.Dsn;
cocoaOptions.EnableAutoSessionTracking = options.AutoSessionTracking;
cocoaOptions.MaxAttachmentSize = (nuint) options.MaxAttachmentSize;
cocoaOptions.MaxBreadcrumbs = (nuint) options.MaxBreadcrumbs;
cocoaOptions.MaxCacheItems = (nuint) options.MaxCacheItems;
cocoaOptions.MaxAttachmentSize = (nuint)options.MaxAttachmentSize;
cocoaOptions.MaxBreadcrumbs = (nuint)options.MaxBreadcrumbs;
cocoaOptions.MaxCacheItems = (nuint)options.MaxCacheItems;
cocoaOptions.ReleaseName = options.Release;
cocoaOptions.SampleRate = options.SampleRate;
cocoaOptions.SendClientReports = options.SendClientReports;
cocoaOptions.SendDefaultPii = options.SendDefaultPii;
cocoaOptions.SessionTrackingIntervalMillis = (nuint) options.AutoSessionTrackingInterval.TotalMilliseconds;
cocoaOptions.SessionTrackingIntervalMillis = (nuint)options.AutoSessionTrackingInterval.TotalMilliseconds;

if (options.Environment is { } environment)
{
Expand Down Expand Up @@ -69,7 +71,7 @@ private static void InitSentryCocoaSdk(SentryOptions options)
}

// These options we have behind feature flags
if (options is {IsPerformanceMonitoringEnabled: true, iOS.EnableCocoaSdkTracing: true})
if (options is { IsPerformanceMonitoringEnabled: true, iOS.EnableCocoaSdkTracing: true })
{
if (options.EnableTracing != null)
{
Expand Down Expand Up @@ -203,4 +205,35 @@ private static string GetDefaultReleaseString()
private static string GetDefaultDistributionString() => GetBundleValue("CFBundleVersion");

private static string GetBundleValue(string key) => NSBundle.MainBundle.ObjectForInfoDictionary(key).ToString();

internal static Dictionary<long, DebugImage> LoadDebugImages(IDiagnosticLogger? logger)
{
logger?.LogDebug("Collecting a list of native debug images.");
var result = new Dictionary<long, DebugImage>();
try
{
var cList = SentryCocoaHybridSdk.DebugImages;
logger?.LogDebug("There are {0} native debug images, parsing the information.", cList.Length);
foreach (var cItem in cList)
{
if (cItem.ImageAddress?.ParseHexAsLong() is { } imageAddress)
{
result.Add(imageAddress, new DebugImage()
{
CodeFile = cItem.CodeFile,
ImageAddress = imageAddress,
// TODO check imageVmAddress
ImageSize = cItem.ImageSize?.LongValue,
DebugId = cItem.DebugID,
Type = cItem.Type,
});
}
}
}
catch (Exception e)
{
logger?.LogWarning("Error loading the list of debug images", e);
}
return result;
}
}

0 comments on commit 1dc256f

Please sign in to comment.