-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RUM-2925 feat: Add backtrace generation capability to DatadogCoreProtocol
#1687
Changes from all commits
70858ac
758bc6d
9f62c3e
17a9b03
4c4fe41
bcd1428
1568791
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2019-Present Datadog, Inc. | ||
*/ | ||
|
||
import XCTest | ||
import DatadogCrashReporting | ||
@testable import DatadogInternal | ||
|
||
/// Tests integration of `DatadogCore` and `DatadogCrashReporting` for backtrace generation. | ||
class GeneratingBacktraceTests: XCTestCase { | ||
private var core: DatadogCoreProxy! // swiftlint:disable:this implicitly_unwrapped_optional | ||
|
||
override func setUp() { | ||
super.setUp() | ||
core = DatadogCoreProxy(context: .mockWith(trackingConsent: .granted)) | ||
} | ||
|
||
override func tearDown() { | ||
core.flushAndTearDown() | ||
core = nil | ||
super.tearDown() | ||
} | ||
|
||
func testGivenCrashReportingIsEnabled_thenCoreCanGenerateBacktrace() throws { | ||
// Given | ||
CrashReporting.enable(in: core) | ||
XCTAssertNotNil(core.get(feature: BacktraceReportingFeature.self), "`BacktraceReportingFeature` is registered") | ||
|
||
// When | ||
let backtrace = try XCTUnwrap(core.backtraceReporter.generateBacktrace()) | ||
|
||
// Then | ||
XCTAssertGreaterThan(backtrace.threads.count, 0, "Some thread(s) should be recorded") | ||
XCTAssertGreaterThan(backtrace.binaryImages.count, 0, "Some binary image(s) should be recorded") | ||
|
||
XCTAssertTrue( | ||
backtrace.stack.contains("DatadogCoreTests"), | ||
"Backtrace stack should include at least one frame from `DatadogCoreTests` image" | ||
) | ||
XCTAssertTrue( | ||
backtrace.stack.contains("XCTest"), | ||
"Backtrace stack should include at least one frame from `XCTest` image" | ||
) | ||
#if os(iOS) | ||
XCTAssertTrue( | ||
backtrace.binaryImages.contains(where: { $0.libraryName == "DatadogCoreTests iOS" }), | ||
"Backtrace should include the image for `DatadogCoreTests iOS`" | ||
) | ||
#elseif os(tvOS) | ||
XCTAssertTrue( | ||
backtrace.binaryImages.contains(where: { $0.libraryName == "DatadogCoreTests tvOS" }), | ||
"Backtrace should include the image for `DatadogCoreTests tvOS`" | ||
) | ||
#endif | ||
XCTAssertTrue( | ||
// Assert on prefix as it is `XCTestCore` on iOS 15+ and `XCTest` earlier: | ||
backtrace.binaryImages.contains(where: { $0.libraryName.hasPrefix("XCTest") }), | ||
"Backtrace should include the image for `XCTest`" | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -229,16 +229,16 @@ extension DatadogCore: DatadogCoreProtocol { | |
/// | ||
/// - Parameter feature: The Feature instance. | ||
func register<T>(feature: T) throws where T: DatadogFeature { | ||
let featureDirectories = try directory.getFeatureDirectories(forFeatureNamed: T.name) | ||
if let feature = feature as? DatadogRemoteFeature { | ||
let featureDirectories = try directory.getFeatureDirectories(forFeatureNamed: T.name) | ||
Comment on lines
-232
to
+233
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was a miss - we need to create storage directories only for |
||
|
||
let performancePreset: PerformancePreset | ||
if let override = feature.performanceOverride { | ||
performancePreset = performance.updated(with: override) | ||
} else { | ||
performancePreset = performance | ||
} | ||
let performancePreset: PerformancePreset | ||
if let override = feature.performanceOverride { | ||
performancePreset = performance.updated(with: override) | ||
} else { | ||
performancePreset = performance | ||
} | ||
|
||
if let feature = feature as? DatadogRemoteFeature { | ||
let storage = FeatureStorage( | ||
featureName: T.name, | ||
queue: readWriteQueue, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2019-Present Datadog, Inc. | ||
*/ | ||
|
||
import XCTest | ||
import DatadogInternal | ||
@testable import DatadogCore | ||
|
||
private struct RemoteFeatureMock: DatadogRemoteFeature { | ||
static let name: String = "remote-feature-mock" | ||
|
||
var requestBuilder: FeatureRequestBuilder = FeatureRequestBuilderMock() | ||
var messageReceiver: FeatureMessageReceiver = NOPFeatureMessageReceiver() | ||
} | ||
|
||
private struct FeatureMock: DatadogFeature { | ||
static let name: String = "feature-mock" | ||
|
||
var messageReceiver: FeatureMessageReceiver = NOPFeatureMessageReceiver() | ||
} | ||
|
||
class DatadogCore_FeatureDirectoriesTests: XCTestCase { | ||
private var core: DatadogCore! // swiftlint:disable:this implicitly_unwrapped_optional | ||
|
||
override func setUp() { | ||
super.setUp() | ||
temporaryCoreDirectory.create() | ||
core = DatadogCore( | ||
directory: temporaryCoreDirectory, | ||
dateProvider: SystemDateProvider(), | ||
initialConsent: .mockRandom(), | ||
performance: .mockRandom(), | ||
httpClient: HTTPClientMock(), | ||
encryption: nil, | ||
contextProvider: .mockAny(), | ||
applicationVersion: .mockAny(), | ||
maxBatchesPerUpload: .mockRandom(min: 1, max: 100), | ||
backgroundTasksEnabled: .mockAny() | ||
) | ||
} | ||
|
||
override func tearDown() { | ||
core.flushAndTearDown() | ||
core = nil | ||
temporaryCoreDirectory.delete() | ||
super.tearDown() | ||
} | ||
|
||
func testWhenRegisteringRemoteFeature_itCreatesFeatureDirectories() throws { | ||
// When | ||
try core.register(feature: RemoteFeatureMock()) | ||
|
||
// Then | ||
let featureDirectory = try temporaryCoreDirectory.coreDirectory.subdirectory(path: RemoteFeatureMock.name) | ||
XCTAssertNoThrow(try featureDirectory.subdirectory(path: "v2"), "Authorized data directory must exist") | ||
XCTAssertNoThrow(try featureDirectory.subdirectory(path: "intermediate-v2"), "Intermediate data directory must exist") | ||
} | ||
|
||
func testWhenRegisteringFeature_itDoesNotCreateFeatureDirectories() throws { | ||
// When | ||
try core.register(feature: FeatureMock()) | ||
|
||
// Then | ||
XCTAssertThrowsError( | ||
try temporaryCoreDirectory.coreDirectory.subdirectory(path: FeatureMock.name), | ||
"Feature directory must not exist" | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,10 @@ public final class CrashReporting { | |
|
||
try core.register(feature: reporter) | ||
|
||
if let plcr = PLCrashReporterPlugin.thirdPartyCrashReporter { | ||
try core.register(backtraceReporter: BacktraceReporter(reporter: plcr)) | ||
} | ||
Comment on lines
+41
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be more elegant, but would require revamping the startup sequence for |
||
|
||
reporter.sendCrashReportIfFound() | ||
|
||
core.telemetry | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2019-Present Datadog, Inc. | ||
*/ | ||
|
||
import DatadogInternal | ||
|
||
internal struct BacktraceReporter: DatadogInternal.BacktraceReporting { | ||
let reporter: ThirdPartyCrashReporter | ||
|
||
func generateBacktrace() -> DatadogInternal.BacktraceReport? { | ||
do { | ||
return try reporter.generateBacktrace() | ||
} catch let error { | ||
DD.logger.error("Encountered an error when generating backtrace", error: error) | ||
return nil | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably worth having this lint rule global to all the tests