Skip to content

Commit

Permalink
Merge pull request #64 from DataDog/ncreated/RUMM-346-fix-crash-on-de…
Browse files Browse the repository at this point in the history
…vice-out-of-memory

RUMM-346 Fix "device out of memory" crash
  • Loading branch information
ncreated authored Apr 6, 2020
2 parents 1091b4e + cc00074 commit e82d85f
Show file tree
Hide file tree
Showing 21 changed files with 266 additions and 45 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
/.build
/.swiftpm
xcuserdata/

# SPM-generated .xcodedproj
Datadog.xcodeproj
8 changes: 8 additions & 0 deletions Datadog.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
40 changes: 40 additions & 0 deletions Datadog/Datadog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@
61133C6E2423990D00786299 /* DatadogExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C472423990D00786299 /* DatadogExtensions.swift */; };
61133C702423993200786299 /* Datadog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61133B82242393DE00786299 /* Datadog.framework */; };
61133C712423993200786299 /* Datadog.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 61133B82242393DE00786299 /* Datadog.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
616A62D024349BA700D1BE12 /* ObjcExceptionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 616A626924335D4900D1BE12 /* ObjcExceptionHandler.m */; };
616A62D62434A3C500D1BE12 /* ObjcExceptionHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 616A626A24335D4900D1BE12 /* ObjcExceptionHandler.h */; };
61C363802436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C3637F2436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift */; };
61C3638324361BE200C4D4E6 /* DatadogPrivateMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C3638224361BE200C4D4E6 /* DatadogPrivateMocks.swift */; };
61C3638524361E9200C4D4E6 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C3638424361E9200C4D4E6 /* Globals.swift */; };
9E08587A242519FF001A3583 /* NetworkPathMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E085879242519FF001A3583 /* NetworkPathMonitor.swift */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -212,6 +217,12 @@
61133C452423990D00786299 /* SwiftExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftExtensions.swift; sourceTree = "<group>"; };
61133C462423990D00786299 /* TestsDirectory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestsDirectory.swift; sourceTree = "<group>"; };
61133C472423990D00786299 /* DatadogExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatadogExtensions.swift; sourceTree = "<group>"; };
616A626924335D4900D1BE12 /* ObjcExceptionHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjcExceptionHandler.m; sourceTree = "<group>"; };
616A626A24335D4900D1BE12 /* ObjcExceptionHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjcExceptionHandler.h; sourceTree = "<group>"; };
616A62DF2434BEF200D1BE12 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
61C3637F2436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjcExceptionHandlerTests.swift; sourceTree = "<group>"; };
61C3638224361BE200C4D4E6 /* DatadogPrivateMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatadogPrivateMocks.swift; sourceTree = "<group>"; };
61C3638424361E9200C4D4E6 /* Globals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Globals.swift; sourceTree = "<group>"; };
9E085879242519FF001A3583 /* NetworkPathMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPathMonitor.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -247,6 +258,7 @@
children = (
61133B9C2423979B00786299 /* Datadog */,
61133C082423983800786299 /* DatadogObjc */,
616A626824335D4900D1BE12 /* DatadogPrivate */,
61133C122423990D00786299 /* DatadogTests */,
61133C07242397F200786299 /* TargetSupport */,
61133B83242393DE00786299 /* Products */,
Expand Down Expand Up @@ -363,6 +375,7 @@
61133BB72423979B00786299 /* Utils */ = {
isa = PBXGroup;
children = (
61C3638424361E9200C4D4E6 /* Globals.swift */,
61133BB82423979B00786299 /* InternalLoggers.swift */,
61133BB92423979B00786299 /* CompilationConditions.swift */,
61133BBA2423979B00786299 /* SwiftExtensions.swift */,
Expand Down Expand Up @@ -455,6 +468,7 @@
children = (
61133C182423990D00786299 /* Datadog */,
61133C132423990D00786299 /* DatadogObjc */,
61C3637E2436163400C4D4E6 /* DatadogPrivate */,
61133C422423990D00786299 /* Matchers */,
61133C442423990D00786299 /* Helpers */,
);
Expand Down Expand Up @@ -495,6 +509,7 @@
61133C1C2423990D00786299 /* UIKitMocks.swift */,
61133C1D2423990D00786299 /* DatadogObjcMocks.swift */,
61133C1F2423990D00786299 /* DatadogMocks.swift */,
61C3638224361BE200C4D4E6 /* DatadogPrivateMocks.swift */,
61133C202423990D00786299 /* FoundationMocks.swift */,
);
path = Mocks;
Expand Down Expand Up @@ -615,6 +630,24 @@
name = Frameworks;
sourceTree = "<group>";
};
616A626824335D4900D1BE12 /* DatadogPrivate */ = {
isa = PBXGroup;
children = (
616A62DF2434BEF200D1BE12 /* module.modulemap */,
616A626A24335D4900D1BE12 /* ObjcExceptionHandler.h */,
616A626924335D4900D1BE12 /* ObjcExceptionHandler.m */,
);
path = DatadogPrivate;
sourceTree = "<group>";
};
61C3637E2436163400C4D4E6 /* DatadogPrivate */ = {
isa = PBXGroup;
children = (
61C3637F2436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift */,
);
path = DatadogPrivate;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand All @@ -623,6 +656,7 @@
buildActionMask = 2147483647;
files = (
61133B93242393DE00786299 /* Datadog.h in Headers */,
616A62D62434A3C500D1BE12 /* ObjcExceptionHandler.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -795,6 +829,8 @@
61133BCC2423979B00786299 /* MobileDevice.swift in Sources */,
9E08587A242519FF001A3583 /* NetworkPathMonitor.swift in Sources */,
61133BCA2423979B00786299 /* EncodableValue.swift in Sources */,
61C3638524361E9200C4D4E6 /* Globals.swift in Sources */,
616A62D024349BA700D1BE12 /* ObjcExceptionHandler.m in Sources */,
61133BE62423979B00786299 /* LogSanitizer.swift in Sources */,
61133BDF2423979B00786299 /* SwiftExtensions.swift in Sources */,
61133BEA2423979B00786299 /* LogConsoleOutput.swift in Sources */,
Expand Down Expand Up @@ -836,9 +872,11 @@
61133C622423990D00786299 /* InternalLoggersTests.swift in Sources */,
61133C582423990D00786299 /* FileWriterTests.swift in Sources */,
61133C672423990D00786299 /* LogConsoleOutputTests.swift in Sources */,
61C3638324361BE200C4D4E6 /* DatadogPrivateMocks.swift in Sources */,
61133C4C2423990D00786299 /* LogsMocks.swift in Sources */,
61133C542423990D00786299 /* NetworkConnectionInfoProviderTests.swift in Sources */,
61133C4A2423990D00786299 /* DDConfigurationTests.swift in Sources */,
61C363802436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift in Sources */,
61133C602423990D00786299 /* HTTPHeadersTests.swift in Sources */,
61133C632423990D00786299 /* DatadogConfigurationTests.swift in Sources */,
61133C572423990D00786299 /* FileReaderTests.swift in Sources */,
Expand Down Expand Up @@ -1042,6 +1080,7 @@
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SUPPORTS_MACCATALYST = NO;
SWIFT_INCLUDE_PATHS = "$(SRCROOT)/DatadogPrivate";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
Expand All @@ -1066,6 +1105,7 @@
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SUPPORTS_MACCATALYST = NO;
SWIFT_INCLUDE_PATHS = "$(SRCROOT)/DatadogPrivate";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
Expand Down
18 changes: 18 additions & 0 deletions Datadog/DatadogPrivate/ObjcExceptionHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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-2020 Datadog, Inc.
*/

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ObjcExceptionHandler : NSObject

- (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error
NS_SWIFT_NAME(rethrowToSwift(tryBlock:));

@end

NS_ASSUME_NONNULL_END
23 changes: 23 additions & 0 deletions Datadog/DatadogPrivate/ObjcExceptionHandler.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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-2020 Datadog, Inc.
*/

#import <Foundation/Foundation.h>
#import "ObjcExceptionHandler.h"

@implementation ObjcExceptionHandler

- (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error {
@try {
tryBlock();
return YES;
}
@catch (NSException *exception) {
*error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:exception.userInfo];
return NO;
}
}

@end
9 changes: 9 additions & 0 deletions Datadog/DatadogPrivate/include/SPMHeaders.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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-2020 Datadog, Inc.
*/


// List of headers for SPM to include for `_Datadog_Private` module.
#import "../ObjcExceptionHandler.h"
1 change: 1 addition & 0 deletions Datadog/DatadogPrivate/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

14 changes: 14 additions & 0 deletions Datadog/DatadogPrivate/module.private.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
Module Map Language reference: https://clang.llvm.org/docs/Modules.html#module-map-language
*/
module _Datadog_Private {
/*
Each header exported from here must be also exposed to each dependency manager separately:
- For Carthage, add it to "Datadog > Build Phases > Headers > Project".
- For SPM, import it from `include/SPMHeaders.h`.
- Cocoapods `.podspec` is already configured to read this `module.modulemap`.
*/

header "ObjcExceptionHandler.h"
export *
}
8 changes: 6 additions & 2 deletions DatadogSDK.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ Pod::Spec.new do |s|
s.swift_version = '5.1'
s.ios.deployment_target = '11.0'

s.source = { :git => 'https://github.com/DataDog/dd-sdk-ios.git', :tag => s.version.to_s }
s.source = { :git => "https://github.com/DataDog/dd-sdk-ios.git", :tag => s.version.to_s }

s.source_files = "Sources/Datadog/**/*.swift"
s.source_files = "Sources/Datadog/**/*.swift", "Datadog/DatadogPrivate/*.m"
s.preserve_paths = "Datadog/DatadogPrivate/*.{modulemap,h}"
s.pod_target_xcconfig = {
"SWIFT_INCLUDE_PATHS" => "$(PODS_ROOT)/DatadogSDK/Datadog/DatadogPrivate/** $(PODS_TARGET_SRCROOT)/DatadogSDK/Datadog/DatadogPrivate/**"
}
end
6 changes: 5 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ let package = Package(
targets: [
.target(
name: "Datadog",
dependencies: []),
dependencies: ["_Datadog_Private"]),
.target(
name: "DatadogObjc",
dependencies: ["Datadog"]),
.target(
name: "_Datadog_Private",
path: "Datadog/DatadogPrivate"
),
.testTarget(
name: "DatadogTests",
dependencies: ["Datadog", "DatadogObjc"]),
Expand Down
8 changes: 4 additions & 4 deletions Sources/Datadog/Core/Persistence/FileWriter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ internal final class FileWriter {
let file = try orchestrator.getWritableFile(writeSize: UInt64(data.count))

if try file.size() == 0 {
try file.append { write in
write(data)
try file.append { (write: (Data) throws -> Void) in
try write(data)
}
} else {
try file.append { write in
write(commaSeparatorData)
write(data)
try write(commaSeparatorData)
try write(data)
}
}
} catch {
Expand Down
32 changes: 15 additions & 17 deletions Sources/Datadog/Core/Persistence/Files/File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import Foundation
import _Datadog_Private

/// Provides convenient interface for reading metadata and appending data to the file.
internal protocol WritableFile {
Expand All @@ -15,7 +16,7 @@ internal protocol WritableFile {
func size() throws -> UInt64

/// Synchronously appends given data at the end of this file.
func append(transaction: ((Data) -> Void) -> Void) throws
func append(transaction: ((Data) throws -> Void) throws -> Void) throws
}

/// Provides convenient interface for reading contents and metadata of the file.
Expand All @@ -42,27 +43,24 @@ internal struct File: WritableFile, ReadableFile {
}

/// Appends given data at the end of this file.
func append(transaction: ((Data) -> Void) -> Void) throws {
func append(transaction: ((Data) throws -> Void) throws -> Void) throws {
let fileHandle = try FileHandle(forWritingTo: url)
defer { fileHandle.closeFile() }
fileHandle.seekToEndOfFile()

try objcExceptionHandler.rethrowToSwift {
fileHandle.seekToEndOfFile()
}

// Writes given data at the end of the file.
func appendData(_ data: Data) {
/*
Apple documentation https://developer.apple.com/documentation/foundation/filehandle/1410936-write says
that `fileHandle.write()` raises an exception if no free space is left on the file system,
or if any other writing error occurs. Those are unchecked exceptions impossible to handle in Swift.

It was already escalated to Apple in Swift open source project discussion:
https://forums.swift.org/t/pitch-replacement-for-filehandle/5177

Until better replacement is provided by Apple, the SDK sticks to this API. To mitigate the risk of
crashing client applications, other precautions are implemented carefuly.
*/
fileHandle.write(data)
func appendData(_ data: Data) throws {
try objcExceptionHandler.rethrowToSwift {
fileHandle.write(data)
}
}

try transaction { chunkOfData in
try appendData(chunkOfData)
}
transaction(appendData)
}

func read() throws -> Data {
Expand Down
5 changes: 0 additions & 5 deletions Sources/Datadog/Logs/LogOutputs/LogConsoleOutput.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ internal protocol ConsoleLogFormatter {
func format(log: Log) -> String
}

/// Function printing `String` content to console.
internal var consolePrint: (String) -> Void = { content in
print(content)
}

/// `LogOutput` which prints logs to console.
internal struct LogConsoleOutput: LogOutput {
/// Time formatter used for `.short` output format.
Expand Down
15 changes: 15 additions & 0 deletions Sources/Datadog/Utils/Globals.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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-2020 Datadog, Inc.
*/

import _Datadog_Private

/// Function printing `String` content to console.
internal var consolePrint: (String) -> Void = { content in
print(content)
}

/// Exception handler rethrowing `NSExceptions` to Swift `NSError`.
internal var objcExceptionHandler = ObjcExceptionHandler()
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class FileReaderTests: XCTestCase {
)
_ = try temporaryDirectory
.createFile(named: .mockAnyFileName())
.append { write in write("ABCD".utf8Data) }
.append { write in try write("ABCD".utf8Data) }

let batch = reader.readNextBatch()

Expand All @@ -46,13 +46,13 @@ class FileReaderTests: XCTestCase {
queue: queue
)
let file1 = try temporaryDirectory.createFile(named: dateProvider.currentDate().toFileName)
try file1.append { write in write("1".utf8Data) }
try file1.append { write in try write("1".utf8Data) }

let file2 = try temporaryDirectory.createFile(named: dateProvider.currentDate().toFileName)
try file2.append { write in write("2".utf8Data) }
try file2.append { write in try write("2".utf8Data) }

let file3 = try temporaryDirectory.createFile(named: dateProvider.currentDate().toFileName)
try file3.append { write in write("3".utf8Data) }
try file3.append { write in try write("3".utf8Data) }

var batch: Batch
batch = try reader.readNextBatch().unwrapOrThrow()
Expand Down
Loading

0 comments on commit e82d85f

Please sign in to comment.