From db70657391c8b1dc7bededdc32b7d68b3c38efa4 Mon Sep 17 00:00:00 2001 From: Yehor Popovych Date: Fri, 15 Nov 2024 17:45:51 +0000 Subject: [PATCH 1/4] fixed code coverage file size problem --- Sources/CDatadogSDKTesting/SymbolAddress.c | 6 ++--- .../Coverage/DDCoverageHelper.swift | 23 +++++++++++++++---- .../ImageSymbols/BinaryImages.swift | 9 +++++--- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Sources/CDatadogSDKTesting/SymbolAddress.c b/Sources/CDatadogSDKTesting/SymbolAddress.c index 5df28493..aeaeb505 100644 --- a/Sources/CDatadogSDKTesting/SymbolAddress.c +++ b/Sources/CDatadogSDKTesting/SymbolAddress.c @@ -23,7 +23,7 @@ void *FindSymbolInImage(const char *symbol, const struct mach_header *image, int struct segment_command_64 *cur_seg_cmd; uintptr_t cur = (uintptr_t)image + sizeof(struct mach_header_64); - for (uint i = 0; i < image->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + for (unsigned int i = 0; i < image->ncmds; i++, cur += cur_seg_cmd->cmdsize) { cur_seg_cmd = (struct segment_command_64 *)cur; if (cur_seg_cmd->cmd == LC_SEGMENT_64) { if (!strcmp(cur_seg_cmd->segname, SEG_TEXT)) { @@ -62,7 +62,7 @@ void *FindSymbolInImage(const char *symbol, const struct mach_header *image, int struct segment_command *cur_seg_cmd; uintptr_t cur = (uintptr_t)image + sizeof(struct mach_header); - for (uint i = 0; i < image->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + for (unsigned int i = 0; i < image->ncmds; i++, cur += cur_seg_cmd->cmdsize) { cur_seg_cmd = (struct segment_command *)cur; if (cur_seg_cmd->cmd == LC_SEGMENT) { if (!strcmp(cur_seg_cmd->segname, SEG_TEXT)) { @@ -84,7 +84,7 @@ void *FindSymbolInImage(const char *symbol, const struct mach_header *image, int char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); struct nlist *sym; - int index; + unsigned int index; for (index = 0, sym = symtab; index < symtab_cmd->nsyms; index += 1, sym += 1) { if (sym->n_un.n_strx != 0 && !strcmp(symbol, strtab + sym->n_un.n_strx)) { uint64_t address = slide + sym->n_value; diff --git a/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift b/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift index 5fd8fd3a..b02f508f 100644 --- a/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift +++ b/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift @@ -122,10 +122,8 @@ final class DDCoverageHelper { private func profileSetFilename(url: URL) { setenv("LLVM_PROFILE_FILE", url.path, 1) BinaryImages.profileImages.forEach { - if $0.profileInitializeFileFuncPtr != nil { - let llvm_profile_initialize_file = unsafeBitCast($0.profileInitializeFileFuncPtr, to: cFunc.self) - llvm_profile_initialize_file() - } + let llvm_profile_initialize_file = unsafeBitCast($0.profileInitializeFileFuncPtr, to: cFunc.self) + llvm_profile_initialize_file() } } @@ -181,11 +179,26 @@ final class DDCoverageHelper { static func load() -> Bool { guard let llvmProfilePath = profileGetFileName() else { return false } Log.debug("DDCoverageHelper patching environment: \(llvmProfilePath)") - let newEnv = llvmProfilePath.replacingOccurrences(of: "%c", with: "") + var newEnv = llvmProfilePath.replacingOccurrences(of: "%c", with: "") + if newEnv.range(of: "%m") == nil { + newEnv = newEnv.replacingOccurrences(of: ".profraw", with: "%m.profraw") + } setenv("LLVM_PROFILE_FILE", newEnv, 1) + if newEnv != llvmProfilePath { + BinaryImages.profileImages.forEach { + let set_page_size = unsafeBitCast($0.setPageSizeFuncPtr, + to: (@convention(c) (UInt) -> Void).self) + set_page_size(0) + } + BinaryImages.profileImages.forEach { + let llvm_profile_initialize_file = unsafeBitCast($0.profileInitializeFileFuncPtr, to: cFunc.self) + llvm_profile_initialize_file() + } + } Log.debug("DDCoverageHelper patched environment") return true } private static let nameNotAllowed: CharacterSet = .alphanumerics.union(.init(charactersIn: "-._")).inverted + private static var continuousModeEnabled: Bool = false } diff --git a/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift b/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift index 8592bedb..cdc4964a 100644 --- a/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift +++ b/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift @@ -22,7 +22,8 @@ struct ProfileInfoImage { let endCountersFuncPtr: UnsafeMutableRawPointer let beginDataFuncPtr: UnsafeMutableRawPointer let endDataFuncPtr: UnsafeMutableRawPointer - let profileInitializeFileFuncPtr: UnsafeMutableRawPointer? + let profileInitializeFileFuncPtr: UnsafeMutableRawPointer + let setPageSizeFuncPtr: UnsafeMutableRawPointer } struct BinaryImages { @@ -61,7 +62,8 @@ struct BinaryImages { let end_counters_symbol = FindSymbolInImage("___llvm_profile_end_counters", header, slide), let begin_data_symbol = FindSymbolInImage("___llvm_profile_begin_data", header, slide), let end_data_symbol = FindSymbolInImage("___llvm_profile_end_data", header, slide), - let profile_initialize_symbol = FindSymbolInImage("___llvm_profile_initialize", header, slide) + let profile_initialize_symbol = FindSymbolInImage("___llvm_profile_initialize", header, slide), + let set_pagesize_symbol = FindSymbolInImage("___llvm_profile_set_page_size", header, slide) { let profileImage = ProfileInfoImage(name: name, path: path, @@ -70,7 +72,8 @@ struct BinaryImages { endCountersFuncPtr: end_counters_symbol, beginDataFuncPtr: begin_data_symbol, endDataFuncPtr: end_data_symbol, - profileInitializeFileFuncPtr: profile_initialize_symbol) + profileInitializeFileFuncPtr: profile_initialize_symbol, + setPageSizeFuncPtr: set_pagesize_symbol) profileImages.append(profileImage) } } From 3f0bb6626ad77f794b1c08a0ee2f4374a2c6ed29 Mon Sep 17 00:00:00 2001 From: Yehor Popovych Date: Thu, 12 Dec 2024 18:36:20 +0100 Subject: [PATCH 2/4] switched code coverage to the new library --- DatadogSDKTesting.xcodeproj/project.pbxproj | 163 +++++----- .../CDatadogSDKTesting/CDatadogSDKTesting.h | 2 - Sources/CDatadogSDKTesting/CodeCoverage.cpp | 289 ------------------ Sources/CDatadogSDKTesting/CoverageExporter.h | 48 --- .../CoverageExporterJson.cpp | 111 ------- .../CDatadogSDKTesting/CoverageExporterJson.h | 36 --- Sources/CDatadogSDKTesting/CoverageReport.cpp | 116 ------- Sources/CDatadogSDKTesting/CoverageReport.h | 57 ---- .../CoverageSummaryInfo.cpp | 67 ---- .../CDatadogSDKTesting/CoverageSummaryInfo.h | 188 ------------ Sources/CDatadogSDKTesting/Error.cpp | 164 ---------- Sources/CDatadogSDKTesting/SymbolAddress.c | 162 ---------- .../CDatadogSDKTesting/include/CodeCoverage.h | 17 -- .../include/SymbolAddress.h | 19 -- Sources/DatadogSDKTesting/Config.swift | 9 +- .../Coverage/CoverageSettings.swift | 35 --- .../Coverage/DDCoverageHelper.swift | 172 ++++------- Sources/DatadogSDKTesting/DDTest.swift | 20 +- Sources/DatadogSDKTesting/DDTestModule.swift | 4 +- Sources/DatadogSDKTesting/DDTestMonitor.swift | 13 +- .../Environment/EnvironmentKeys.swift | 1 - .../FrameworkLoadHandler.swift | 8 - .../ImageSymbols/BinaryImages.swift | 52 +--- .../ImageSymbols/DDSymbolicator.swift | 6 +- .../Utils/PlatformUtils.swift | 18 +- .../CoverageExporter/CoverageExporter.swift | 67 ++-- .../DDCoverageConversor.swift | 50 --- .../CoverageExporter/DDCoverageFormat.swift | 117 ------- .../CoverageExporter/TestCodeCoverage.swift | 76 +++++ Sources/EventsExporter/EventsExporter.swift | 11 +- Sources/EventsExporter/Utils/Spawn.swift | 12 +- .../EarlyFlakeDetectionTests.swift | 6 +- .../CodeCoverageModelTests.swift | 78 +++++ 33 files changed, 391 insertions(+), 1803 deletions(-) delete mode 100644 Sources/CDatadogSDKTesting/CodeCoverage.cpp delete mode 100644 Sources/CDatadogSDKTesting/CoverageExporter.h delete mode 100644 Sources/CDatadogSDKTesting/CoverageExporterJson.cpp delete mode 100644 Sources/CDatadogSDKTesting/CoverageExporterJson.h delete mode 100644 Sources/CDatadogSDKTesting/CoverageReport.cpp delete mode 100644 Sources/CDatadogSDKTesting/CoverageReport.h delete mode 100644 Sources/CDatadogSDKTesting/CoverageSummaryInfo.cpp delete mode 100644 Sources/CDatadogSDKTesting/CoverageSummaryInfo.h delete mode 100644 Sources/CDatadogSDKTesting/Error.cpp delete mode 100644 Sources/CDatadogSDKTesting/SymbolAddress.c delete mode 100644 Sources/CDatadogSDKTesting/include/CodeCoverage.h delete mode 100644 Sources/CDatadogSDKTesting/include/SymbolAddress.h delete mode 100644 Sources/EventsExporter/CoverageExporter/DDCoverageConversor.swift delete mode 100644 Sources/EventsExporter/CoverageExporter/DDCoverageFormat.swift create mode 100644 Sources/EventsExporter/CoverageExporter/TestCodeCoverage.swift create mode 100644 Tests/EventsExporter/CodeCoverageModelTests.swift diff --git a/DatadogSDKTesting.xcodeproj/project.pbxproj b/DatadogSDKTesting.xcodeproj/project.pbxproj index f6a4b27c..df90e2ba 100644 --- a/DatadogSDKTesting.xcodeproj/project.pbxproj +++ b/DatadogSDKTesting.xcodeproj/project.pbxproj @@ -37,6 +37,9 @@ A71E647E2BAA0AA100F2ACA5 /* EnvironmentReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71E64682BAA0AA100F2ACA5 /* EnvironmentReader.swift */; }; A71E64802BAB442300F2ACA5 /* ConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71E647F2BAB442300F2ACA5 /* ConfigTests.swift */; }; A74246002BFF94FA00F7EC64 /* ITRSkippabble.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74245FF2BFF94FA00F7EC64 /* ITRSkippabble.swift */; }; + A74EB8E02D0AE8A600FAD9B8 /* CodeCoverage in Frameworks */ = {isa = PBXBuildFile; productRef = A74EB8DF2D0AE8A600FAD9B8 /* CodeCoverage */; }; + A74EB8E42D0AFD3C00FAD9B8 /* CodeCoverageModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74EB8E32D0AFD3C00FAD9B8 /* CodeCoverageModelTests.swift */; }; + A74EB8E62D0B449300FAD9B8 /* TestCodeCoverage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74EB8E52D0B449300FAD9B8 /* TestCodeCoverage.swift */; }; A76174BD2CB3E3780098CE99 /* DDXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = A76174BC2CB3E3780098CE99 /* DDXCTestCase.swift */; }; A76174BF2CB45F9B0098CE99 /* XCTestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A76174BE2CB45F9B0098CE99 /* XCTestExtensions.swift */; }; A76174C22CB685390098CE99 /* SettingsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A76174C02CB685250098CE99 /* SettingsService.swift */; }; @@ -51,21 +54,9 @@ A7CC6D862C07D624003C13BC /* Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7CC6D852C07D624003C13BC /* Tags.swift */; }; A7CC6D882C0F3F83003C13BC /* Tags+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7CC6D872C0F3F83003C13BC /* Tags+ObjC.swift */; }; A7D23AF82C9B10A8006AB41D /* SpanMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D23AF72C9B10A8006AB41D /* SpanMetadata.swift */; }; + A7D7E6502D074D9200B49C35 /* CodeCoverage in Frameworks */ = {isa = PBXBuildFile; productRef = A7D7E64F2D074D9200B49C35 /* CodeCoverage */; }; A7E0056D2C766EF7004D4B78 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7E0056C2C766EF7004D4B78 /* CoreTelephony.framework */; platformFilters = (ios, maccatalyst, macos, ); }; - A7E232E52BC6E80A0087D8F8 /* CodeCoverage.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E232E42BC6E80A0087D8F8 /* CodeCoverage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A7E232F52BC6EB470087D8F8 /* CoverageExporterJson.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7E232EA2BC6EB460087D8F8 /* CoverageExporterJson.cpp */; }; - A7E232F62BC6EB470087D8F8 /* CoverageReport.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E232EB2BC6EB460087D8F8 /* CoverageReport.h */; }; - A7E232F72BC6EB470087D8F8 /* CoverageSummaryInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7E232EC2BC6EB460087D8F8 /* CoverageSummaryInfo.cpp */; }; - A7E232F92BC6EB470087D8F8 /* CoverageSummaryInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E232EE2BC6EB460087D8F8 /* CoverageSummaryInfo.h */; }; - A7E232FB2BC6EB470087D8F8 /* CoverageExporter.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E232F02BC6EB470087D8F8 /* CoverageExporter.h */; }; - A7E232FC2BC6EB470087D8F8 /* CodeCoverage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7E232F12BC6EB470087D8F8 /* CodeCoverage.cpp */; }; - A7E232FD2BC6EB470087D8F8 /* Error.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7E232F22BC6EB470087D8F8 /* Error.cpp */; }; - A7E232FE2BC6EB470087D8F8 /* CoverageReport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7E232F32BC6EB470087D8F8 /* CoverageReport.cpp */; }; - A7E232FF2BC6EB470087D8F8 /* CoverageExporterJson.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E232F42BC6EB470087D8F8 /* CoverageExporterJson.h */; }; - A7E233032BC6F01B0087D8F8 /* SymbolAddress.c in Sources */ = {isa = PBXBuildFile; fileRef = A7E233022BC6F01B0087D8F8 /* SymbolAddress.c */; }; - A7E233052BC6F03C0087D8F8 /* SymbolAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E233042BC6F03C0087D8F8 /* SymbolAddress.h */; settings = {ATTRIBUTES = (Public, ); }; }; A7E233072BC6F46F0087D8F8 /* CDatadogSDKTesting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7E232DC2BC6E68B0087D8F8 /* CDatadogSDKTesting.framework */; }; - A7E2330D2BC6F4780087D8F8 /* llvm-xcframework in Frameworks */ = {isa = PBXBuildFile; productRef = A7E2330C2BC6F4780087D8F8 /* llvm-xcframework */; }; A7E2330F2BC6F4E70087D8F8 /* CDatadogSDKTesting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7E232DC2BC6E68B0087D8F8 /* CDatadogSDKTesting.framework */; }; A7E233172BC7FA590087D8F8 /* CDatadogSDKTesting.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E233162BC7FA590087D8F8 /* CDatadogSDKTesting.h */; settings = {ATTRIBUTES = (Public, ); }; }; A7E2332D2BC81B5D0087D8F8 /* AutoLoad.c in Sources */ = {isa = PBXBuildFile; fileRef = A7E233002BC6EF220087D8F8 /* AutoLoad.c */; }; @@ -125,9 +116,7 @@ A7FC26202BA1EC6200067E26 /* SearchCommitFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = E146BC3E2869C3BB0057C37B /* SearchCommitFormat.swift */; }; A7FC26222BA1EC6200067E26 /* ITRService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E146BC372865D55A0057C37B /* ITRService.swift */; }; A7FC262E2BA1ECBD00067E26 /* LLVMSimpleCoverageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10A7E0D2806D68F00B464C9 /* LLVMSimpleCoverageFormat.swift */; }; - A7FC262F2BA1ECBD00067E26 /* DDCoverageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C5838280D809900A31763 /* DDCoverageFormat.swift */; }; A7FC26302BA1ECBD00067E26 /* CoverageExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1743F5628490141006CBFF8 /* CoverageExporter.swift */; }; - A7FC26312BA1ECBD00067E26 /* DDCoverageConversor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15C79672722AD0500E9DBAD /* DDCoverageConversor.swift */; }; A7FC26322BA1ECC500067E26 /* SpansExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E143CEFE27D7790200F4018A /* SpansExporter.swift */; }; A7FC26332BA1ECC500067E26 /* SpanEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E143CEFF27D7790200F4018A /* SpanEncoder.swift */; }; A7FC26342BA1ECC500067E26 /* SpanSanitizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBBD4627F1FFE70059BC3E /* SpanSanitizer.swift */; }; @@ -334,6 +323,8 @@ A71E64682BAA0AA100F2ACA5 /* EnvironmentReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnvironmentReader.swift; sourceTree = ""; }; A71E647F2BAB442300F2ACA5 /* ConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigTests.swift; sourceTree = ""; }; A74245FF2BFF94FA00F7EC64 /* ITRSkippabble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ITRSkippabble.swift; sourceTree = ""; }; + A74EB8E32D0AFD3C00FAD9B8 /* CodeCoverageModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeCoverageModelTests.swift; sourceTree = ""; }; + A74EB8E52D0B449300FAD9B8 /* TestCodeCoverage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCodeCoverage.swift; sourceTree = ""; }; A76174BC2CB3E3780098CE99 /* DDXCTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDXCTestCase.swift; sourceTree = ""; }; A76174BE2CB45F9B0098CE99 /* XCTestExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestExtensions.swift; sourceTree = ""; }; A76174C02CB685250098CE99 /* SettingsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsService.swift; sourceTree = ""; }; @@ -351,19 +342,7 @@ A7D23AF72C9B10A8006AB41D /* SpanMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpanMetadata.swift; sourceTree = ""; }; A7E0056C2C766EF7004D4B78 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; A7E232DC2BC6E68B0087D8F8 /* CDatadogSDKTesting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CDatadogSDKTesting.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - A7E232E42BC6E80A0087D8F8 /* CodeCoverage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CodeCoverage.h; sourceTree = ""; }; - A7E232EA2BC6EB460087D8F8 /* CoverageExporterJson.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoverageExporterJson.cpp; sourceTree = ""; }; - A7E232EB2BC6EB460087D8F8 /* CoverageReport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoverageReport.h; sourceTree = ""; }; - A7E232EC2BC6EB460087D8F8 /* CoverageSummaryInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoverageSummaryInfo.cpp; sourceTree = ""; }; - A7E232EE2BC6EB460087D8F8 /* CoverageSummaryInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoverageSummaryInfo.h; sourceTree = ""; }; - A7E232F02BC6EB470087D8F8 /* CoverageExporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoverageExporter.h; sourceTree = ""; }; - A7E232F12BC6EB470087D8F8 /* CodeCoverage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CodeCoverage.cpp; sourceTree = ""; }; - A7E232F22BC6EB470087D8F8 /* Error.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Error.cpp; sourceTree = ""; }; - A7E232F32BC6EB470087D8F8 /* CoverageReport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoverageReport.cpp; sourceTree = ""; }; - A7E232F42BC6EB470087D8F8 /* CoverageExporterJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoverageExporterJson.h; sourceTree = ""; }; A7E233002BC6EF220087D8F8 /* AutoLoad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = AutoLoad.c; sourceTree = ""; }; - A7E233022BC6F01B0087D8F8 /* SymbolAddress.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SymbolAddress.c; sourceTree = ""; }; - A7E233042BC6F03C0087D8F8 /* SymbolAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolAddress.h; sourceTree = ""; }; A7E233162BC7FA590087D8F8 /* CDatadogSDKTesting.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDatadogSDKTesting.h; sourceTree = ""; }; A7E233202BC819A70087D8F8 /* AutoLoad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutoLoad.h; sourceTree = ""; }; A7E233322BC93D100087D8F8 /* Synced.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Synced.swift; sourceTree = ""; }; @@ -399,7 +378,6 @@ E126DD2C253F366200700A40 /* StdoutCapture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StdoutCapture.swift; sourceTree = ""; }; E126DD2E253F367600700A40 /* StderrCapture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StderrCapture.swift; sourceTree = ""; }; E1293F8828DB515F00D31DD6 /* SanitizerHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SanitizerHelper.swift; sourceTree = ""; }; - E12C5838280D809900A31763 /* DDCoverageFormat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DDCoverageFormat.swift; sourceTree = ""; }; E137366128A3BE3600420E26 /* IntelligentTestRunner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntelligentTestRunner.swift; sourceTree = ""; }; E13D0BC92546E29C00C37D56 /* SignalUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalUtils.swift; sourceTree = ""; }; E141CF5F256FD513008B6B70 /* PlatformUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformUtils.swift; sourceTree = ""; }; @@ -470,7 +448,6 @@ E15264AE25FA3C16008B8665 /* DDSymbolicatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDSymbolicatorTests.swift; sourceTree = ""; }; E1550C44274F7FF700006F11 /* Clock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clock.swift; sourceTree = ""; }; E158E2DB25471DA000DBCD6C /* DDInstrumentationControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDInstrumentationControl.swift; sourceTree = ""; }; - E15C79672722AD0500E9DBAD /* DDCoverageConversor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDCoverageConversor.swift; sourceTree = ""; }; E160A11125275CC1003121A5 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; E1665A7225D12D7500BA53CD /* Spawn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Spawn.swift; sourceTree = ""; }; E1665A8025D13BBB00BA53CD /* DDSymbolicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDSymbolicator.swift; sourceTree = ""; }; @@ -522,7 +499,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A7E2330D2BC6F4780087D8F8 /* llvm-xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -534,6 +510,7 @@ A7FC26532BA1F1E600067E26 /* URLSessionInstrumentation in Frameworks */, A7E233312BC82CD10087D8F8 /* Kronos in Frameworks */, A7FC26502BA1F1E600067E26 /* CrashReporter in Frameworks */, + A74EB8E02D0AE8A600FAD9B8 /* CodeCoverage in Frameworks */, A7FC26512BA1F1E600067E26 /* SigmaSwiftStatistics in Frameworks */, A7FC26522BA1F1E600067E26 /* InMemoryExporter in Frameworks */, A7FC26552BA1F1F900067E26 /* EventsExporter.framework in Frameworks */, @@ -556,6 +533,7 @@ A7FC264E2BA1EFEC00067E26 /* OpenTelemetrySdk in Frameworks */, A7FC26F52BA867BA00067E26 /* OpenTelemetryApi in Frameworks */, A7E2330F2BC6F4E70087D8F8 /* CDatadogSDKTesting.framework in Frameworks */, + A7D7E6502D074D9200B49C35 /* CodeCoverage in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -648,17 +626,7 @@ children = ( A7E232E32BC6E7E90087D8F8 /* include */, A7E233162BC7FA590087D8F8 /* CDatadogSDKTesting.h */, - A7E232F12BC6EB470087D8F8 /* CodeCoverage.cpp */, - A7E232F02BC6EB470087D8F8 /* CoverageExporter.h */, - A7E232EA2BC6EB460087D8F8 /* CoverageExporterJson.cpp */, - A7E232F42BC6EB470087D8F8 /* CoverageExporterJson.h */, - A7E232F32BC6EB470087D8F8 /* CoverageReport.cpp */, - A7E232EB2BC6EB460087D8F8 /* CoverageReport.h */, - A7E232EC2BC6EB460087D8F8 /* CoverageSummaryInfo.cpp */, - A7E232EE2BC6EB460087D8F8 /* CoverageSummaryInfo.h */, - A7E232F22BC6EB470087D8F8 /* Error.cpp */, A7E233002BC6EF220087D8F8 /* AutoLoad.c */, - A7E233022BC6F01B0087D8F8 /* SymbolAddress.c */, A7E233382BCD6B240087D8F8 /* SubprocessSpawn.c */, A7B39C6F2BCFCD77007F98B2 /* SubprocessWait.c */, ); @@ -669,8 +637,6 @@ isa = PBXGroup; children = ( A7E233202BC819A70087D8F8 /* AutoLoad.h */, - A7E233042BC6F03C0087D8F8 /* SymbolAddress.h */, - A7E232E42BC6E80A0087D8F8 /* CodeCoverage.h */, A7E233362BCD6A000087D8F8 /* SubprocessSpawn.h */, A7B39C712BCFD234007F98B2 /* SubprocessWait.h */, ); @@ -754,6 +720,7 @@ E141EAFB27EB3B9E0057D747 /* Upload */, A7FC26E42BA8648300067E26 /* EventsExporter.xctestplan */, E141EAE727EB3B9E0057D747 /* EventsExporterTests.swift */, + A74EB8E32D0AFD3C00FAD9B8 /* CodeCoverageModelTests.swift */, ); path = EventsExporter; sourceTree = ""; @@ -967,10 +934,9 @@ E1743F552849012A006CBFF8 /* CoverageExporter */ = { isa = PBXGroup; children = ( - E15C79672722AD0500E9DBAD /* DDCoverageConversor.swift */, - E12C5838280D809900A31763 /* DDCoverageFormat.swift */, E10A7E0D2806D68F00B464C9 /* LLVMSimpleCoverageFormat.swift */, E1743F5628490141006CBFF8 /* CoverageExporter.swift */, + A74EB8E52D0B449300FAD9B8 /* TestCodeCoverage.swift */, ); path = CoverageExporter; sourceTree = ""; @@ -1162,14 +1128,8 @@ files = ( A7E2332E2BC81B680087D8F8 /* AutoLoad.h in Headers */, A7E233172BC7FA590087D8F8 /* CDatadogSDKTesting.h in Headers */, - A7E232E52BC6E80A0087D8F8 /* CodeCoverage.h in Headers */, A7E233372BCD6A000087D8F8 /* SubprocessSpawn.h in Headers */, A7B39C722BCFD234007F98B2 /* SubprocessWait.h in Headers */, - A7E233052BC6F03C0087D8F8 /* SymbolAddress.h in Headers */, - A7E232FF2BC6EB470087D8F8 /* CoverageExporterJson.h in Headers */, - A7E232FB2BC6EB470087D8F8 /* CoverageExporter.h in Headers */, - A7E232F62BC6EB470087D8F8 /* CoverageReport.h in Headers */, - A7E232F92BC6EB470087D8F8 /* CoverageSummaryInfo.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1205,7 +1165,6 @@ ); name = CDatadogSDKTesting; packageProductDependencies = ( - A7E2330C2BC6F4780087D8F8 /* llvm-xcframework */, ); productName = CDatadogSDKTesting; productReference = A7E232DC2BC6E68B0087D8F8 /* CDatadogSDKTesting.framework */; @@ -1220,10 +1179,12 @@ A7FC25612BA1E83500067E26 /* Frameworks */, A7FC25622BA1E83500067E26 /* Resources */, A7E2332C2BC81A880087D8F8 /* Embed Frameworks */, + A74EB8E22D0AEAA000FAD9B8 /* Copy CodeCoverage Framework */, ); buildRules = ( ); dependencies = ( + A74EB8DE2D0A010A00FAD9B8 /* PBXTargetDependency */, A7FC26FD2BA87E5100067E26 /* PBXTargetDependency */, A7FC26FB2BA87E4A00067E26 /* PBXTargetDependency */, A7FC26F92BA87E4000067E26 /* PBXTargetDependency */, @@ -1238,6 +1199,7 @@ E1BE9B5B2732B2A300B92AD4 /* InMemoryExporter */, E1BE9B5D2732B2A300B92AD4 /* URLSessionInstrumentation */, A7E233302BC82CD10087D8F8 /* Kronos */, + A74EB8DF2D0AE8A600FAD9B8 /* CodeCoverage */, ); productName = DatadogSDKTesting; productReference = A7FC25642BA1E83500067E26 /* DatadogSDKTesting.framework */; @@ -1273,6 +1235,7 @@ buildRules = ( ); dependencies = ( + A74EB8DC2D0A00D400FAD9B8 /* PBXTargetDependency */, A7FC26F32BA867B000067E26 /* PBXTargetDependency */, A7FC26EF2BA8676A00067E26 /* PBXTargetDependency */, A7E233122BC6F4E70087D8F8 /* PBXTargetDependency */, @@ -1281,6 +1244,7 @@ packageProductDependencies = ( E1DCAF6A284A1FE000F44BE3 /* OpenTelemetrySdk */, A7FC26F42BA867BA00067E26 /* OpenTelemetryApi */, + A7D7E64F2D074D9200B49C35 /* CodeCoverage */, ); productName = EventsExporter; productReference = A7FC25802BA1E87400067E26 /* EventsExporter.framework */; @@ -1395,8 +1359,8 @@ E17B7AA6253F02580081D7F3 /* XCRemoteSwiftPackageReference "SigmaSwiftStatistics" */, E12248DA26455DEB00305968 /* XCRemoteSwiftPackageReference "opentelemetry-swift" */, E141EB5727EB44400057D747 /* XCRemoteSwiftPackageReference "swift-nio" */, - E1E51D4E28B5099200E0E206 /* XCRemoteSwiftPackageReference "llvm-xcframework" */, A7E2332F2BC82CD10087D8F8 /* XCRemoteSwiftPackageReference "Kronos" */, + A7D7E64E2D074D9200B49C35 /* XCRemoteSwiftPackageReference "swift-code-coverage" */, ); productRefGroup = E1ABA04B25231E2500ED8AEF /* Products */; projectDirPath = ""; @@ -1466,20 +1430,36 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + A74EB8E22D0AEAA000FAD9B8 /* Copy CodeCoverage Framework */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Copy CodeCoverage Framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "FRAMEWORKS_DIR=\"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}\"\nmkdir -p \"${FRAMEWORKS_DIR}\"\ncp -rf \"${BUILT_PRODUCTS_DIR}/CodeCoverage.framework\" \"${FRAMEWORKS_DIR}/\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ A7E232D82BC6E68B0087D8F8 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A7E232FC2BC6EB470087D8F8 /* CodeCoverage.cpp in Sources */, - A7E232FD2BC6EB470087D8F8 /* Error.cpp in Sources */, A7E2332D2BC81B5D0087D8F8 /* AutoLoad.c in Sources */, - A7E232F72BC6EB470087D8F8 /* CoverageSummaryInfo.cpp in Sources */, - A7E232FE2BC6EB470087D8F8 /* CoverageReport.cpp in Sources */, - A7E233032BC6F01B0087D8F8 /* SymbolAddress.c in Sources */, A7E233392BCD6B240087D8F8 /* SubprocessSpawn.c in Sources */, A7B39C702BCFCD77007F98B2 /* SubprocessWait.c in Sources */, - A7E232F52BC6EB470087D8F8 /* CoverageExporterJson.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1612,7 +1592,6 @@ A7FC263A2BA1ECCF00067E26 /* EncodableValue.swift in Sources */, A7FC26352BA1ECCF00067E26 /* PerformancePreset.swift in Sources */, A7FC261B2BA1EC5A00067E26 /* DataUploadWorker.swift in Sources */, - A7FC26312BA1ECBD00067E26 /* DDCoverageConversor.swift in Sources */, A7FC26372BA1ECCF00067E26 /* JSONGeneric.swift in Sources */, A7FC26222BA1EC6200067E26 /* ITRService.swift in Sources */, A7FC26172BA1EC5A00067E26 /* DatadogEndpoints.swift in Sources */, @@ -1623,11 +1602,11 @@ A718EC312CBE754300C5F0D9 /* EarlyFlakeDetectionService.swift in Sources */, A7FC261F2BA1EC6200067E26 /* SkipTestsFormat.swift in Sources */, A76174C22CB685390098CE99 /* SettingsService.swift in Sources */, + A74EB8E62D0B449300FAD9B8 /* TestCodeCoverage.swift in Sources */, A7FC26192BA1EC5A00067E26 /* DataUploadDelay.swift in Sources */, A7FC264A2BA1ECF800067E26 /* File.swift in Sources */, A7FC264D2BA1ED1200067E26 /* EventsExporter.swift in Sources */, A7FC26302BA1ECBD00067E26 /* CoverageExporter.swift in Sources */, - A7FC262F2BA1ECBD00067E26 /* DDCoverageFormat.swift in Sources */, A7FC263B2BA1ECCF00067E26 /* Feature.swift in Sources */, A7FC26342BA1ECC500067E26 /* SpanSanitizer.swift in Sources */, A7FC26382BA1ECCF00067E26 /* Spawn.swift in Sources */, @@ -1671,6 +1650,7 @@ A7FC266F2BA1F7FA00067E26 /* SpanSanitizerTests.swift in Sources */, A7FC26842BA1F81D00067E26 /* Encoding.swift in Sources */, A7FC26762BA1F80000067E26 /* SpawnTests.swift in Sources */, + A74EB8E42D0AFD3C00FAD9B8 /* CodeCoverageModelTests.swift in Sources */, A7FC267F2BA1F81D00067E26 /* TestsDirectory.swift in Sources */, A7FC26892BA1F81D00067E26 /* ServerMock.swift in Sources */, A7FC26832BA1F81D00067E26 /* CoreMocks.swift in Sources */, @@ -1703,6 +1683,14 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + A74EB8DC2D0A00D400FAD9B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = A74EB8DB2D0A00D400FAD9B8 /* CodeCoverage */; + }; + A74EB8DE2D0A010A00FAD9B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = A74EB8DD2D0A010A00FAD9B8 /* CodeCoverage */; + }; A7B39C6E2BCFC7CE007F98B2 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = A7E232DB2BC6E68B0087D8F8 /* CDatadogSDKTesting */; @@ -1799,7 +1787,7 @@ "@loader_path/Frameworks", ); MACH_O_TYPE = staticlib; - MARKETING_VERSION = "2.5.2"; + MARKETING_VERSION = 2.5.2; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CDatadogSDKTesting; @@ -1839,7 +1827,7 @@ "@loader_path/Frameworks", ); MACH_O_TYPE = staticlib; - MARKETING_VERSION = "2.5.2"; + MARKETING_VERSION = 2.5.2; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CDatadogSDKTesting; @@ -1865,6 +1853,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_MODULE_VERIFIER = YES; ENABLE_TESTING_SEARCH_PATHS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GENERATE_INFOPLIST_FILE = YES; @@ -1907,6 +1896,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_MODULE_VERIFIER = YES; ENABLE_TESTING_SEARCH_PATHS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = "© 2020-Present Datadog, Inc."; @@ -2194,7 +2184,7 @@ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_LSApplicationCategoryType = ""; - MARKETING_VERSION = "2.5.2"; + MARKETING_VERSION = 2.5.2; PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.IntegrationTestsUITests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -2218,7 +2208,7 @@ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_LSApplicationCategoryType = ""; - MARKETING_VERSION = "2.5.2"; + MARKETING_VERSION = 2.5.2; PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.IntegrationTestsUITests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -2292,7 +2282,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = "2.5.2"; + MARKETING_VERSION = 2.5.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -2364,7 +2354,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 11.0; LLVM_LTO = YES_THIN; MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = "2.5.2"; + MARKETING_VERSION = 2.5.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SWIFT_COMPILATION_MODE = wholemodule; @@ -2457,6 +2447,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + A7D7E64E2D074D9200B49C35 /* XCRemoteSwiftPackageReference "swift-code-coverage" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/DataDog/swift-code-coverage.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.1.1; + }; + }; A7E2332F2BC82CD10087D8F8 /* XCRemoteSwiftPackageReference "Kronos" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/MobileNativeFoundation/Kronos.git"; @@ -2469,8 +2467,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/open-telemetry/opentelemetry-swift.git"; requirement = { - kind = revision; - revision = f745a6435bad737dcb29807cd451326f63327a87; + kind = upToNextMinorVersion; + minimumVersion = 1.12.1; }; }; E141EB5727EB44400057D747 /* XCRemoteSwiftPackageReference "swift-nio" */ = { @@ -2505,14 +2503,6 @@ minimumVersion = 9.0.2; }; }; - E1E51D4E28B5099200E0E206 /* XCRemoteSwiftPackageReference "llvm-xcframework" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/DataDog/llvm-xcframework"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 15.0.0; - }; - }; E1FB4839253891650083B263 /* XCRemoteSwiftPackageReference "plcrashreporter" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/microsoft/plcrashreporter.git"; @@ -2524,10 +2514,25 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - A7E2330C2BC6F4780087D8F8 /* llvm-xcframework */ = { + A74EB8DB2D0A00D400FAD9B8 /* CodeCoverage */ = { + isa = XCSwiftPackageProductDependency; + package = A7D7E64E2D074D9200B49C35 /* XCRemoteSwiftPackageReference "swift-code-coverage" */; + productName = CodeCoverage; + }; + A74EB8DD2D0A010A00FAD9B8 /* CodeCoverage */ = { + isa = XCSwiftPackageProductDependency; + package = A7D7E64E2D074D9200B49C35 /* XCRemoteSwiftPackageReference "swift-code-coverage" */; + productName = CodeCoverage; + }; + A74EB8DF2D0AE8A600FAD9B8 /* CodeCoverage */ = { + isa = XCSwiftPackageProductDependency; + package = A7D7E64E2D074D9200B49C35 /* XCRemoteSwiftPackageReference "swift-code-coverage" */; + productName = CodeCoverage; + }; + A7D7E64F2D074D9200B49C35 /* CodeCoverage */ = { isa = XCSwiftPackageProductDependency; - package = E1E51D4E28B5099200E0E206 /* XCRemoteSwiftPackageReference "llvm-xcframework" */; - productName = "llvm-xcframework"; + package = A7D7E64E2D074D9200B49C35 /* XCRemoteSwiftPackageReference "swift-code-coverage" */; + productName = CodeCoverage; }; A7E233302BC82CD10087D8F8 /* Kronos */ = { isa = XCSwiftPackageProductDependency; diff --git a/Sources/CDatadogSDKTesting/CDatadogSDKTesting.h b/Sources/CDatadogSDKTesting/CDatadogSDKTesting.h index caa91875..07813a7f 100644 --- a/Sources/CDatadogSDKTesting/CDatadogSDKTesting.h +++ b/Sources/CDatadogSDKTesting/CDatadogSDKTesting.h @@ -7,7 +7,5 @@ #pragma once #include -#include #include #include -#include diff --git a/Sources/CDatadogSDKTesting/CodeCoverage.cpp b/Sources/CDatadogSDKTesting/CodeCoverage.cpp deleted file mode 100644 index 32df455a..00000000 --- a/Sources/CDatadogSDKTesting/CodeCoverage.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/* - * 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 2020-2022 Datadog, Inc. - * - * - * CodeCoverage.cpp - * Derived from: - * - * CodeCoverage.cpp - * LLVM - */ - -#include "include/CodeCoverage.h" -#include "CoverageExporterJson.h" -#include "CoverageSummaryInfo.h" -#include "llvm/ProfileData/Coverage/CoverageMapping.h" -#include "llvm/ProfileData/Coverage/CoverageMappingReader.h" -#include "llvm/ProfileData/InstrProfReader.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" - -#include - -using namespace llvm; -using namespace coverage; - -namespace { - -thread_local static std::vector> CoverageReaders; -/// The implementation of the coverage tool. -class CodeCoverageTool { -public: -std::string getCoverageJson(std::string profdata, std::vector &covFilenames); - -private: -/// Print the error message to the error output stream. -void error(const Twine &Message, StringRef Whence = ""); - -/// Print the warning message to the error output stream. -void warning(const Twine &Message, StringRef Whence = ""); - -/// Convert \p Path into an absolute path and append it to the list -/// of collected paths. -void addCollectedPath(const std::string &Path); - -/// If \p Path is a regular file, collect the path. If it's a -/// directory, recursively collect all of the paths within the directory. -void collectPaths(const std::string &Path); - -/// Retrieve a file status with a cache. -Optional getFileStatus(StringRef FilePath); - -/// Load the coverage mapping data. Return nullptr if an error occurred. -std::unique_ptr load(); - -std::vector ObjectFilenames; - -/// The path to the indexed profile. -std::string PGOFilename; - -/// A list of input source files. -std::vector SourceFiles; - -/// File status cache used when finding the same file. -StringMap > FileStatusCache; - -/// The architecture the coverage mapping data targets. -std::vector CoverageArches; - - /// Added for DDSDKSwiftTesting efficency - std::unique_ptr loadCached(); -}; -} - -static std::string getErrorString(const Twine &Message, StringRef Whence, - bool Warning) { - std::string Str = (Warning ? "warning" : "error"); - Str += ": "; - if (!Whence.empty()) - Str += Whence.str() + ": "; - Str += Message.str() + "\n"; - return Str; -} - -void CodeCoverageTool::addCollectedPath(const std::string &Path) { - SmallString<128> EffectivePath(Path); - if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) { - error(EC.message(), Path); - return; - } - sys::path::remove_dots(EffectivePath, /*remove_dot_dot=*/ true); - SourceFiles.emplace_back(EffectivePath.str()); -} - -void CodeCoverageTool::collectPaths(const std::string &Path) { - llvm::sys::fs::file_status Status; - llvm::sys::fs::status(Path, Status); - if (!llvm::sys::fs::exists(Status)) { - warning("Source file doesn't exist, proceeded by ignoring it.", Path); - return; - } - - if (llvm::sys::fs::is_regular_file(Status)) { - addCollectedPath(Path); - return; - } - - if (llvm::sys::fs::is_directory(Status)) { - std::error_code EC; - for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E; - F != E; F.increment(EC)) { - - auto Status = F->status(); - if (!Status) { - warning(Status.getError().message(), F->path()); - continue; - } - - if (Status->type() == llvm::sys::fs::file_type::regular_file) - addCollectedPath(F->path()); - } - } -} - -Optional -CodeCoverageTool::getFileStatus(StringRef FilePath) { - auto It = FileStatusCache.try_emplace(FilePath); - auto &CachedStatus = It.first->getValue(); - if (!It.second) - return CachedStatus; - - sys::fs::file_status Status; - if (!sys::fs::status(FilePath, Status)) - CachedStatus = Status; - return CachedStatus; -} - -std::unique_ptr CodeCoverageTool::load() { - auto CoverageOrErr = - CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches,""); - if (Error E = CoverageOrErr.takeError()) { - - error("Failed to load coverage: " + toString(std::move(E)), - join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); - return nullptr; - } - auto Coverage = std::move(CoverageOrErr.get()); - unsigned Mismatched = Coverage->getMismatchedCount(); - if (Mismatched) { - warning(Twine(Mismatched) + " functions have mismatched data"); - } - - return Coverage; -} - -// If E is a no_data_found error, returns success. Otherwise returns E. -static Error handleMaybeNoDataFoundError(Error E) { - return handleErrors( - std::move(E), [](const CoverageMapError &CME) { - if (CME.get() == coveragemap_error::no_data_found) - return static_cast(Error::success()); - return make_error(CME.get()); - }); -} - -//LLVM structs recreated -typedef struct BinaryCoverageReaderStruct { - void *vTable; - std::vector Filenames; - std::vector MappingRecords; - InstrProfSymtab ProfileNames; - size_t CurrentRecord; - std::vector FunctionsFilenames; - std::vector Expressions; - std::vector MappingRegions; - std::unique_ptr FuncRecords; -} BinaryCoverageReaderStruct; - -std::unique_ptr CodeCoverageTool::loadCached() { - - auto ProfileReaderOrErr = IndexedInstrProfReader::create(PGOFilename); - if (Error E = ProfileReaderOrErr.takeError()){ - error("Failed to load coverage: " + toString(std::move(E)), PGOFilename); - return nullptr; - } - auto ProfileReader = std::move(ProfileReaderOrErr.get()); - - if( CoverageReaders.empty() ) { - for (const auto &File : llvm::enumerate(ObjectFilenames)) { - auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN( - File.value(), /*IsText=*/false, /*RequiresNullTerminator=*/false); - if (std::error_code EC = CovMappingBufOrErr.getError()) { - return nullptr; - } - StringRef Arch = CoverageArches.empty() ? StringRef() : CoverageArches[File.index()]; - MemoryBufferRef CovMappingBufRef = - CovMappingBufOrErr.get()->getMemBufferRef(); - SmallVector, 4> Buffers; - auto CoverageReadersOrErr = BinaryCoverageReader::create(CovMappingBufRef, Arch, Buffers, ""); - if (Error E = CoverageReadersOrErr.takeError()) { - E = handleMaybeNoDataFoundError(std::move(E)); - if (E) { - return nullptr; - } - // E == success (originally a no_data_found error). - continue; - } - - for (auto &Reader : CoverageReadersOrErr.get()){ - CoverageReaders.push_back(std::move(Reader)); - } - } - } else { - for (auto &Reader : CoverageReaders) { - BinaryCoverageReaderStruct* fakeReader = (BinaryCoverageReaderStruct *)Reader.get(); - fakeReader->CurrentRecord = 0; - } - } - - auto Readers = ArrayRef(CoverageReaders); - auto CoverageOrErr = CoverageMapping::load(Readers, *ProfileReader); - if (Error E = CoverageOrErr.takeError()) { - - error("Failed to load coverage: " + toString(std::move(E)), - join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); - return nullptr; - } - auto Coverage = std::move(CoverageOrErr.get()); - unsigned Mismatched = Coverage->getMismatchedCount(); - if (Mismatched) { - warning(Twine(Mismatched) + " functions have mismatched data"); - } - return Coverage; -} - -std::string CodeCoverageTool::getCoverageJson(std::string profdata, std::vector &covFilenames) { - - PGOFilename = profdata; - - for (const std::string &Filename : covFilenames) { - ObjectFilenames.emplace_back(Filename); - } - - std::string outputString; - raw_string_ostream string_stream(outputString); - - - auto Coverage = loadCached(); - if (!Coverage) { - printf("Could not load coverage information"); - return outputString; - } - - std::unique_ptr Exporter; - - Exporter = std::make_unique(*Coverage.get(), string_stream); - - if (SourceFiles.empty()) - Exporter->renderRoot(); - else - Exporter->renderRoot(SourceFiles); - - return outputString; -} - -void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { - std::cout << getErrorString(Message, Whence, false); -} - -void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) { - std::cout << getErrorString(Message, Whence, true); -} - -extern "C" const char* LLVMCoverageInfoForProfile(const char* prof_data, const char* const* images, unsigned int image_count) { - std::vector vectorList; - vectorList.reserve(image_count); - - for (unsigned int i = 0; i < image_count; i++) { - vectorList.push_back(std::string(images[i])); - } - - std::string result = CodeCoverageTool().getCoverageJson(std::string(prof_data), vectorList); - char* copied = new char[result.size()+1]; - result.copy(copied, result.size()); - copied[result.size()] = '\0'; - - return copied; -} diff --git a/Sources/CDatadogSDKTesting/CoverageExporter.h b/Sources/CDatadogSDKTesting/CoverageExporter.h deleted file mode 100644 index 9707cda7..00000000 --- a/Sources/CDatadogSDKTesting/CoverageExporter.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 2020-2022 Datadog, Inc. - * - * - * CoverageExporter.h - * Derived from: - * - * CoverageExporter.h - * LLVM - */ - -#ifndef LLVM_COV_COVERAGEEXPORTER_H -#define LLVM_COV_COVERAGEEXPORTER_H - -#include "llvm/ProfileData/Coverage/CoverageMapping.h" - -namespace llvm { - -/// Exports the code coverage information. -class CoverageExporter { -protected: -/// The full CoverageMapping object to export. -const coverage::CoverageMapping &Coverage; - -/// Output stream to print to. -raw_ostream &OS; -// std::string &OS; - -CoverageExporter(const coverage::CoverageMapping &CoverageMapping, raw_ostream &OS) - : Coverage(CoverageMapping), OS(OS) { -} - -public: -virtual ~CoverageExporter(){ -}; - -/// Render the CoverageMapping object. -virtual void renderRoot() = 0; - -/// Render the CoverageMapping object for specified source files. -virtual void renderRoot(ArrayRef SourceFiles) = 0; -}; - -} // end namespace llvm - -#endif // LLVM_COV_COVERAGEEXPORTER_H diff --git a/Sources/CDatadogSDKTesting/CoverageExporterJson.cpp b/Sources/CDatadogSDKTesting/CoverageExporterJson.cpp deleted file mode 100644 index 3835e6c6..00000000 --- a/Sources/CDatadogSDKTesting/CoverageExporterJson.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 2020-2022 Datadog, Inc. - * - * - * CoverageExporterJson.cpp - * Derived from: - * - * CoverageExporterJson.cpp - * LLVM - */ - -#include "CoverageExporterJson.h" -#include "llvm/Support/JSON.h" -#include "llvm/Support/ThreadPool.h" - -/// The semantic version combined as a string. -#define LLVM_COVERAGE_EXPORT_JSON_STR "3.0.1" - -/// Unique type identifier for JSON coverage export. -#define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export" - -using namespace llvm; - -namespace { - -// The JSON library accepts int64_t, but profiling counts are stored as uint64_t. -// Therefore we need to explicitly convert from unsigned to signed, since a naive -// cast is implementation-defined behavior when the unsigned value cannot be -// represented as a signed value. We choose to clamp the values to preserve the -// invariant that counts are always >= 0. -int64_t clamp_uint64_to_int64(uint64_t u) { - return std::min(u, static_cast(std::numeric_limits::max())); -} - -json::Array renderSegment(const coverage::CoverageSegment &Segment) { - return json::Array({Segment.Line, Segment.Col, - clamp_uint64_to_int64(Segment.Count), Segment.HasCount, - Segment.IsRegionEntry, Segment.IsGapRegion}); -} - - -json::Array renderFileSegments(const coverage::CoverageData &FileCoverage) { - json::Array SegmentArray; - for (const auto &Segment : FileCoverage) - SegmentArray.push_back(renderSegment(Segment)); - return SegmentArray; -} - -json::Object renderFile(const coverage::CoverageMapping &Coverage, - const std::string &Filename) { - json::Object File({{"filename", Filename}}); - auto FileCoverage = Coverage.getCoverageForFile(Filename); - File["segments"] = renderFileSegments(FileCoverage); - return File; -} - -json::Array renderFiles(const coverage::CoverageMapping &Coverage, - ArrayRef SourceFiles) { - ThreadPoolStrategy S = heavyweight_hardware_concurrency(SourceFiles.size()); - S.Limit = true; - - ThreadPool Pool(S); - json::Array FileArray; - std::mutex FileArrayMutex; - - for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) { - auto &SourceFile = SourceFiles[I]; - Pool.async([&] { - auto File = renderFile(Coverage, SourceFile); - { - std::lock_guard Lock(FileArrayMutex); - FileArray.push_back(std::move(File)); - } - }); - } - Pool.wait(); - return FileArray; -} - -} // end anonymous namespace - -void CoverageExporterJson::renderRoot() { - std::vector SourceFiles; - for (StringRef SF : Coverage.getUniqueSourceFiles()) { - SourceFiles.emplace_back(SF); - } - renderRoot(SourceFiles); -} - -void CoverageExporterJson::renderRoot(ArrayRef SourceFiles) { - auto Files = renderFiles(Coverage, SourceFiles); - // Sort files in order of their names. - llvm::sort(Files, [](const json::Value &A, const json::Value &B) { - const json::Object *ObjA = A.getAsObject(); - const json::Object *ObjB = B.getAsObject(); - assert(ObjA != nullptr && "Value A was not an Object"); - assert(ObjB != nullptr && "Value B was not an Object"); - const StringRef FilenameA = ObjA->getString("filename").getValue(); - const StringRef FilenameB = ObjB->getString("filename").getValue(); - return FilenameA.compare(FilenameB) < 0; - }); - auto Export = json::Object({{"files", std::move(Files)}}); - - auto ExportArray = json::Array({std::move(Export)}); - - OS << json::Object({{"version", LLVM_COVERAGE_EXPORT_JSON_STR}, - {"type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR}, - {"data", std::move(ExportArray)}}); -} diff --git a/Sources/CDatadogSDKTesting/CoverageExporterJson.h b/Sources/CDatadogSDKTesting/CoverageExporterJson.h deleted file mode 100644 index 87bf42f0..00000000 --- a/Sources/CDatadogSDKTesting/CoverageExporterJson.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 2020-2022 Datadog, Inc. - * - * - * CoverageExporterJson.h - * Derived from: - * - * CoverageExporterJson.h - * LLVM - */ - -#ifndef LLVM_COV_COVERAGEEXPORTERJSON_H -#define LLVM_COV_COVERAGEEXPORTERJSON_H - -#include "CoverageExporter.h" - -namespace llvm { - -class CoverageExporterJson : public CoverageExporter { -public: -CoverageExporterJson(const coverage::CoverageMapping &CoverageMapping, raw_ostream &OS) - : CoverageExporter(CoverageMapping, OS) { -} - -/// Render the CoverageMapping object. -void renderRoot() override; - -/// Render the CoverageMapping object for specified source files. -void renderRoot(ArrayRef SourceFiles) override; -}; - -} // end namespace llvm - -#endif // LLVM_COV_COVERAGEEXPORTERJSON_H diff --git a/Sources/CDatadogSDKTesting/CoverageReport.cpp b/Sources/CDatadogSDKTesting/CoverageReport.cpp deleted file mode 100644 index 10d5d4a8..00000000 --- a/Sources/CDatadogSDKTesting/CoverageReport.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 2020-2022 Datadog, Inc. - * - * - * CoverageReport.cpp - * Derived from: - * - * CoverageReport.cpp - * LLVM - */ - -#include "CoverageReport.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/ThreadPool.h" - -using namespace llvm; - -namespace { -/// Get the number of redundant path components in each path in \p Paths. -unsigned getNumRedundantPathComponents(ArrayRef Paths) { - // To start, set the number of redundant path components to the maximum - // possible value. - SmallVector FirstPathComponents{sys::path::begin(Paths[0]), - sys::path::end(Paths[0])}; - unsigned NumRedundant = FirstPathComponents.size(); - - for (unsigned I = 1, E = Paths.size(); NumRedundant > 0 && I < E; ++I) { - StringRef Path = Paths[I]; - for (const auto &Component : - enumerate(make_range(sys::path::begin(Path), sys::path::end(Path)))) { - // Do not increase the number of redundant components: that would remove - // useful parts of already-visited paths. - if (Component.index() >= NumRedundant) - break; - - // Lower the number of redundant components when there's a mismatch - // between the first path, and the path under consideration. - if (FirstPathComponents[Component.index()] != Component.value()) { - NumRedundant = Component.index(); - break; - } - } - } - - return NumRedundant; -} - -/// Determine the length of the longest redundant prefix of the paths in -/// \p Paths. -unsigned getRedundantPrefixLen(ArrayRef Paths) { - // If there's at most one path, no path components are redundant. - if (Paths.size() <= 1) - return 0; - - unsigned PrefixLen = 0; - unsigned NumRedundant = getNumRedundantPathComponents(Paths); - auto Component = sys::path::begin(Paths[0]); - for (unsigned I = 0; I < NumRedundant; ++I) { - auto LastComponent = Component; - ++Component; - PrefixLen += Component - LastComponent; - } - return PrefixLen; -} - -} // end anonymous namespace - -namespace llvm { -void CoverageReport::prepareSingleFileReport(const StringRef Filename, - const coverage::CoverageMapping *Coverage, const unsigned LCP, - FileCoverageSummary *FileReport) { - for (const auto &Group : Coverage->getInstantiationGroups(Filename)) { - std::vector InstantiationSummaries; - for (const coverage::FunctionRecord *F : Group.getInstantiations()) { - auto InstantiationSummary = FunctionCoverageSummary::get(*Coverage, *F); - FileReport->addInstantiation(InstantiationSummary); - InstantiationSummaries.push_back(InstantiationSummary); - } - if (InstantiationSummaries.empty()) - continue; - - auto GroupSummary = - FunctionCoverageSummary::get(Group, InstantiationSummaries); - - FileReport->addFunction(GroupSummary); - } -} - -std::vector CoverageReport::prepareFileReports( - const coverage::CoverageMapping &Coverage, FileCoverageSummary &Totals, - ArrayRef Files) { - unsigned LCP = getRedundantPrefixLen(Files); - - ThreadPoolStrategy S = heavyweight_hardware_concurrency(Files.size()); - S.Limit = true; - ThreadPool Pool(S); - - std::vector FileReports; - FileReports.reserve(Files.size()); - - for (StringRef Filename : Files) { - FileReports.emplace_back(Filename.drop_front(LCP)); - Pool.async(&CoverageReport::prepareSingleFileReport, Filename, - &Coverage, LCP, &FileReports.back()); - } - Pool.wait(); - - for (const auto &FileReport : FileReports) - Totals += FileReport; - - return FileReports; -} - -} // end namespace llvm diff --git a/Sources/CDatadogSDKTesting/CoverageReport.h b/Sources/CDatadogSDKTesting/CoverageReport.h deleted file mode 100644 index 39926170..00000000 --- a/Sources/CDatadogSDKTesting/CoverageReport.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 2020-2022 Datadog, Inc. - * - * - * CoverageReport.h - * Derived from: - * - * CoverageReport.h - * LLVM - */ - -#ifndef LLVM_COV_COVERAGEREPORT_H -#define LLVM_COV_COVERAGEREPORT_H - -#include "CoverageSummaryInfo.h" - -namespace llvm { - -/// Displays the code coverage report. -class CoverageReport { -const coverage::CoverageMapping &Coverage; - -void render(const FileCoverageSummary &File, raw_ostream &OS) const; -void render(const FunctionCoverageSummary &Function, const DemangleCache &DC, - raw_ostream &OS) const; - -public: -CoverageReport(const coverage::CoverageMapping &Coverage) - : Coverage(Coverage) { -} - -void renderFunctionReports(ArrayRef Files, - const DemangleCache &DC, raw_ostream &OS); - -/// Prepare file reports for the files specified in \p Files. -static std::vector -prepareFileReports(const coverage::CoverageMapping &Coverage, - FileCoverageSummary &Totals, ArrayRef Files); - -static void -prepareSingleFileReport(const StringRef Filename, - const coverage::CoverageMapping *Coverage, - const unsigned LCP, - FileCoverageSummary *FileReport); - -/// Render file reports for every unique file in the coverage mapping. -void renderFileReports(raw_ostream &OS) const; - -/// Render file reports for the files specified in \p Files. -void renderFileReports(raw_ostream &OS, ArrayRef Files) const; -}; - -} // end namespace llvm - -#endif // LLVM_COV_COVERAGEREPORT_H diff --git a/Sources/CDatadogSDKTesting/CoverageSummaryInfo.cpp b/Sources/CDatadogSDKTesting/CoverageSummaryInfo.cpp deleted file mode 100644 index debcd11a..00000000 --- a/Sources/CDatadogSDKTesting/CoverageSummaryInfo.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 2020-2022 Datadog, Inc. - * - * - * CoverageSummaryInfo.cpp - * Derived from: - * - * CoverageSummaryInfo.cpp - * LLVM - */ - -#include "CoverageSummaryInfo.h" - -using namespace llvm; -using namespace coverage; - -FunctionCoverageSummary -FunctionCoverageSummary::get(const CoverageMapping &CM, - const coverage::FunctionRecord &Function) { - // Compute the region coverage. - size_t NumCodeRegions = 0, CoveredRegions = 0; - for (auto &CR : Function.CountedRegions) { - if (CR.Kind != CounterMappingRegion::CodeRegion) - continue; - ++NumCodeRegions; - if (CR.ExecutionCount != 0) - ++CoveredRegions; - } - - // Compute the line coverage - size_t NumLines = 0, CoveredLines = 0; - CoverageData CD = CM.getCoverageForFunction(Function); - for (const auto &LCS : getLineCoverageStats(CD)) { - if (!LCS.isMapped()) - continue; - ++NumLines; - if (LCS.getExecutionCount()) - ++CoveredLines; - } - - return FunctionCoverageSummary( - Function.Name, Function.ExecutionCount, - RegionCoverageInfo(CoveredRegions, NumCodeRegions)); -} - -FunctionCoverageSummary -FunctionCoverageSummary::get(const InstantiationGroup &Group, - ArrayRef Summaries) { - std::string Name; - if (Group.hasName()) { - Name = std::string(Group.getName()); - } else { - llvm::raw_string_ostream OS(Name); - OS << "Definition at line " << Group.getLine() << ", column " - << Group.getColumn(); - } - - FunctionCoverageSummary Summary(Name); - Summary.ExecutionCount = Group.getTotalExecutionCount(); - Summary.RegionCoverage = Summaries[0].RegionCoverage; - for (const auto &FCS : Summaries.drop_front()) { - Summary.RegionCoverage.merge(FCS.RegionCoverage); - } - return Summary; -} diff --git a/Sources/CDatadogSDKTesting/CoverageSummaryInfo.h b/Sources/CDatadogSDKTesting/CoverageSummaryInfo.h deleted file mode 100644 index 1d23fe0a..00000000 --- a/Sources/CDatadogSDKTesting/CoverageSummaryInfo.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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 2020-2022 Datadog, Inc. - * - * - * CoverageSummaryInfo.h - * Derived from: - * - * CoverageSummaryInfo.h - * LLVM - */ - - -#ifndef LLVM_COV_COVERAGESUMMARYINFO_H -#define LLVM_COV_COVERAGESUMMARYINFO_H - -#include "llvm/ProfileData/Coverage/CoverageMapping.h" - -namespace llvm { - -/// Provides information about region coverage for a function/file. -class RegionCoverageInfo { -/// The number of regions that were executed at least once. -size_t Covered; - -/// The total number of regions in a function/file. -size_t NumRegions; - -public: -RegionCoverageInfo() : Covered(0), NumRegions(0) { -} - -RegionCoverageInfo(size_t Covered, size_t NumRegions) - : Covered(Covered), NumRegions(NumRegions) { - assert(Covered <= NumRegions && "Covered regions over-counted"); -} - -RegionCoverageInfo &operator+=(const RegionCoverageInfo &RHS) { - Covered += RHS.Covered; - NumRegions += RHS.NumRegions; - return *this; -} - -void merge(const RegionCoverageInfo &RHS) { - Covered = std::max(Covered, RHS.Covered); - NumRegions = std::max(NumRegions, RHS.NumRegions); -} - -size_t getCovered() const { - return Covered; -} - -size_t getNumRegions() const { - return NumRegions; -} - -bool isFullyCovered() const { - return Covered == NumRegions; -} - -double getPercentCovered() const { - assert(Covered <= NumRegions && "Covered regions over-counted"); - if (NumRegions == 0) - return 0.0; - return double(Covered) / double(NumRegions) * 100.0; -} -}; - -/// Provides information about function coverage for a file. -class FunctionCoverageInfo { -/// The number of functions that were executed. -size_t Executed; - -/// The total number of functions in this file. -size_t NumFunctions; - -public: -FunctionCoverageInfo() : Executed(0), NumFunctions(0) { -} - -FunctionCoverageInfo(size_t Executed, size_t NumFunctions) - : Executed(Executed), NumFunctions(NumFunctions) { -} - -FunctionCoverageInfo &operator+=(const FunctionCoverageInfo &RHS) { - Executed += RHS.Executed; - NumFunctions += RHS.NumFunctions; - return *this; -} - -void addFunction(bool Covered) { - if (Covered) - ++Executed; - ++NumFunctions; -} - -size_t getExecuted() const { - return Executed; -} - -size_t getNumFunctions() const { - return NumFunctions; -} - -bool isFullyCovered() const { - return Executed == NumFunctions; -} - -double getPercentCovered() const { - assert(Executed <= NumFunctions && "Covered functions over-counted"); - if (NumFunctions == 0) - return 0.0; - return double(Executed) / double(NumFunctions) * 100.0; -} -}; - -/// A summary of function's code coverage. -struct FunctionCoverageSummary { - std::string Name; - uint64_t ExecutionCount; - RegionCoverageInfo RegionCoverage; - - FunctionCoverageSummary(const std::string &Name) - : Name(Name), ExecutionCount(0) { - } - - FunctionCoverageSummary(const std::string &Name, uint64_t ExecutionCount, - const RegionCoverageInfo &RegionCoverage) - : Name(Name), ExecutionCount(ExecutionCount), - RegionCoverage(RegionCoverage) { - } - - /// Compute the code coverage summary for the given function coverage - /// mapping record. - static FunctionCoverageSummary get(const coverage::CoverageMapping &CM, - const coverage::FunctionRecord &Function); - - /// Compute the code coverage summary for an instantiation group \p Group, - /// given a list of summaries for each instantiation in \p Summaries. - static FunctionCoverageSummary - get(const coverage::InstantiationGroup &Group, - ArrayRef Summaries); -}; - -/// A summary of file's code coverage. -struct FileCoverageSummary { - StringRef Name; - RegionCoverageInfo RegionCoverage; - FunctionCoverageInfo FunctionCoverage; - FunctionCoverageInfo InstantiationCoverage; - - FileCoverageSummary(StringRef Name) : Name(Name) { - } - - FileCoverageSummary &operator+=(const FileCoverageSummary &RHS) { - RegionCoverage += RHS.RegionCoverage; - FunctionCoverage += RHS.FunctionCoverage; - InstantiationCoverage += RHS.InstantiationCoverage; - return *this; - } - - void addFunction(const FunctionCoverageSummary &Function) { - RegionCoverage += Function.RegionCoverage; - FunctionCoverage.addFunction(/*Covered=*/ Function.ExecutionCount > 0); - } - - void addInstantiation(const FunctionCoverageSummary &Function) { - InstantiationCoverage.addFunction(/*Covered=*/ Function.ExecutionCount > 0); - } -}; - -/// A cache for demangled symbols. -struct DemangleCache { - StringMap DemangledNames; - - /// Demangle \p Sym if possible. Otherwise, just return \p Sym. - StringRef demangle(StringRef Sym) const { - const auto DemangledName = DemangledNames.find(Sym); - if (DemangledName == DemangledNames.end()) - return Sym; - return DemangledName->getValue(); - } -}; - -} // namespace llvm - -#endif // LLVM_COV_COVERAGESUMMARYINFO_H diff --git a/Sources/CDatadogSDKTesting/Error.cpp b/Sources/CDatadogSDKTesting/Error.cpp deleted file mode 100644 index b178be93..00000000 --- a/Sources/CDatadogSDKTesting/Error.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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 2020-2022 Datadog, Inc. - * - * - * Error.cpp - * Derived from: - * - * lib/Support/Error.cpp - * LLVM - */ - -#include "llvm/Support/Error.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/ManagedStatic.h" -#include - -using namespace llvm; - -namespace { - -enum class ErrorErrorCode : int { - MultipleErrors = 1, - FileError, - InconvertibleError -}; - -// FIXME: This class is only here to support the transition to llvm::Error. It -// will be removed once this transition is complete. Clients should prefer to -// deal with the Error value directly, rather than converting to error_code. -class ErrorErrorCategory : public std::error_category { -public: -const char *name() const noexcept override { - return "Error"; -} - -std::string message(int condition) const override { - switch (static_cast(condition)) { - case ErrorErrorCode::MultipleErrors: - return "Multiple errors"; - case ErrorErrorCode::InconvertibleError: - return "Inconvertible error value. An error has occurred that could " - "not be converted to a known std::error_code. Please file a " - "bug."; - case ErrorErrorCode::FileError: - return "A file error occurred."; - } - llvm_unreachable("Unhandled error code"); -} -}; - -} - -static ManagedStatic ErrorErrorCat; - -namespace llvm { - -void ErrorInfoBase::anchor() { -} -char ErrorInfoBase::ID = 0; -char ErrorList::ID = 0; -void ECError::anchor() { -} -char ECError::ID = 0; -char StringError::ID = 0; -char FileError::ID = 0; - -void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner) { - if (!E) - return; - OS << ErrorBanner; - handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) { - EI.log(OS); - OS << "\n"; - }); -} - - -std::error_code ErrorList::convertToErrorCode() const { - return std::error_code(static_cast(ErrorErrorCode::MultipleErrors), - *ErrorErrorCat); -} - -std::error_code inconvertibleErrorCode() { - return std::error_code(static_cast(ErrorErrorCode::InconvertibleError), - *ErrorErrorCat); -} - -std::error_code FileError::convertToErrorCode() const { - std::error_code NestedEC = Err->convertToErrorCode(); - if (NestedEC == inconvertibleErrorCode()) - return std::error_code(static_cast(ErrorErrorCode::FileError), - *ErrorErrorCat); - return NestedEC; -} - -Error errorCodeToError(std::error_code EC) { - if (!EC) - return Error::success(); - return Error(std::make_unique(ECError(EC))); -} - -std::error_code errorToErrorCode(Error Err) { - std::error_code EC; - handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { - EC = EI.convertToErrorCode(); - }); - if (EC == inconvertibleErrorCode()) - report_fatal_error(Twine(EC.message())); - return EC; -} - -#if LLVM_ENABLE_ABI_BREAKING_CHECKS -void Error::fatalUncheckedError() const { - dbgs() << "Program aborted due to an unhandled Error:\n"; - if (getPtr()) { - getPtr()->log(dbgs()); - dbgs() << "\n"; - }else - dbgs() << "Error value was Success. (Note: Success values must still be " - "checked prior to being destroyed).\n"; - abort(); -} -#endif - -StringError::StringError(std::error_code EC, const Twine &S) - : Msg(S.str()), EC(EC) { -} - -StringError::StringError(const Twine &S, std::error_code EC) - : Msg(S.str()), EC(EC), PrintMsgOnly(true) { -} - -void StringError::log(raw_ostream &OS) const { - if (PrintMsgOnly) { - OS << Msg; - } else { - OS << EC.message(); - if (!Msg.empty()) - OS << (" " + Msg); - } -} - -std::error_code StringError::convertToErrorCode() const { - return EC; -} - -Error createStringError(std::error_code EC, char const *Msg) { - return make_error(Msg, EC); -} - -void report_fatal_error(Error Err, bool GenCrashDiag) { - assert(Err && "report_fatal_error called with success value"); - std::string ErrMsg; - { - raw_string_ostream ErrStream(ErrMsg); - logAllUnhandledErrors(std::move(Err), ErrStream); - } - report_fatal_error(Twine(ErrMsg)); -} - -} // end namespace llvm diff --git a/Sources/CDatadogSDKTesting/SymbolAddress.c b/Sources/CDatadogSDKTesting/SymbolAddress.c deleted file mode 100644 index aeaeb505..00000000 --- a/Sources/CDatadogSDKTesting/SymbolAddress.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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 2020-Present Datadog, Inc. - */ - -#include "include/SymbolAddress.h" - -#include -#include - -void *FindSymbolInImage(const char *symbol, const struct mach_header *image, intptr_t slide) -{ - if ((image == NULL) || (symbol == NULL)) { - return NULL; - } - - struct symtab_command *symtab_cmd = NULL; - - if (image->magic == MH_MAGIC_64) { - struct segment_command_64 *linkedit_segment = NULL; - struct segment_command_64 *text_segment = NULL; - - struct segment_command_64 *cur_seg_cmd; - uintptr_t cur = (uintptr_t)image + sizeof(struct mach_header_64); - for (unsigned int i = 0; i < image->ncmds; i++, cur += cur_seg_cmd->cmdsize) { - cur_seg_cmd = (struct segment_command_64 *)cur; - if (cur_seg_cmd->cmd == LC_SEGMENT_64) { - if (!strcmp(cur_seg_cmd->segname, SEG_TEXT)) { - text_segment = cur_seg_cmd; - } else if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { - linkedit_segment = cur_seg_cmd; - } - } else if (cur_seg_cmd->cmd == LC_SYMTAB) { - symtab_cmd = (struct symtab_command *)cur_seg_cmd; - } - } - - if (!symtab_cmd || !linkedit_segment || !text_segment) { - return NULL; - } - - uintptr_t linkedit_base = (uintptr_t)slide + (uintptr_t)(linkedit_segment->vmaddr - linkedit_segment->fileoff); - struct nlist_64 *symtab = (struct nlist_64 *)(linkedit_base + symtab_cmd->symoff); - char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); - - struct nlist_64 *sym; - int index; - for (index = 0, sym = symtab; index < symtab_cmd->nsyms; index += 1, sym += 1) { - if (sym->n_un.n_strx != 0 && !strcmp(symbol, strtab + sym->n_un.n_strx)) { - uint64_t address = slide + sym->n_value; - if (sym->n_desc & N_ARM_THUMB_DEF) { - return (void *)(address | 1); - } else { - return (void *)(address); - } - } - } - } else { - struct segment_command *linkedit_segment = NULL; - struct segment_command *text_segment = NULL; - - struct segment_command *cur_seg_cmd; - uintptr_t cur = (uintptr_t)image + sizeof(struct mach_header); - for (unsigned int i = 0; i < image->ncmds; i++, cur += cur_seg_cmd->cmdsize) { - cur_seg_cmd = (struct segment_command *)cur; - if (cur_seg_cmd->cmd == LC_SEGMENT) { - if (!strcmp(cur_seg_cmd->segname, SEG_TEXT)) { - text_segment = cur_seg_cmd; - } else if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { - linkedit_segment = cur_seg_cmd; - } - } else if (cur_seg_cmd->cmd == LC_SYMTAB) { - symtab_cmd = (struct symtab_command *)cur_seg_cmd; - } - } - - if (!symtab_cmd || !linkedit_segment || !text_segment) { - return NULL; - } - - uintptr_t linkedit_base = (uintptr_t)slide + (uintptr_t)(linkedit_segment->vmaddr - linkedit_segment->fileoff); - struct nlist *symtab = (struct nlist *)(linkedit_base + symtab_cmd->symoff); - char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); - - struct nlist *sym; - unsigned int index; - for (index = 0, sym = symtab; index < symtab_cmd->nsyms; index += 1, sym += 1) { - if (sym->n_un.n_strx != 0 && !strcmp(symbol, strtab + sym->n_un.n_strx)) { - uint64_t address = slide + sym->n_value; - if (sym->n_desc & N_ARM_THUMB_DEF) { - return (void *)(address | 1); - } else { - return (void *)(address); - } - } - } - } - - return NULL; -} - -//LLVM structs recreated -typedef struct __llvm_profile_data { - const uint64_t NameRef; - const uint64_t FuncHash; - const int *CounterPtr; - const int *FunctionPointer; - int *Values; - const uint32_t NumCounters; - const uint16_t NumValueSites[2]; -} __llvm_profile_data; - -typedef struct ValueProfNode { - // InstrProfValueData VData; - uint64_t Value; - uint64_t Count; - struct ValueProfNode *Next; -} ValueProfNode; - -void ProfileResetCounters(void *beginCounters, void *endCounters, void *beginData, void *endData) -{ - uint64_t * (*llvm_profile_begin_counters_ptr)(void) = beginCounters; - uint64_t * (*llvm_profile_end_counters_ptr)(void) = endCounters; - const __llvm_profile_data * (*llvm_profile_begin_data)(void) = beginData; - const __llvm_profile_data * (*llvm_profile_end_data)(void) = endData; - - uint64_t *I = (*llvm_profile_begin_counters_ptr)(); - uint64_t *E = (*llvm_profile_end_counters_ptr)(); - - memset(I, 0, sizeof(uint64_t) * (E - I)); - - const __llvm_profile_data *DataBegin = (*llvm_profile_begin_data)(); - const __llvm_profile_data *DataEnd = (*llvm_profile_end_data)(); - const __llvm_profile_data *DI; - for (DI = DataBegin; DI < DataEnd; ++DI) { - uint64_t CurrentVSiteCount = 0; - uint32_t VKI, i; - if (!DI->Values) { - continue; - } - - ValueProfNode **ValueCounters = (ValueProfNode **)DI->Values; - - for (VKI = 0; VKI <= 1; ++VKI) { - CurrentVSiteCount += DI->NumValueSites[VKI]; - } - - for (i = 0; i < CurrentVSiteCount; ++i) { - ValueProfNode *CurrentVNode = ValueCounters[i]; - - while (CurrentVNode) { - CurrentVNode->Count = 0; - CurrentVNode = CurrentVNode->Next; - } - } - } -} - - -//extern int __llvm_profile_runtime; -//int __llvm_profile_runtime = 1; diff --git a/Sources/CDatadogSDKTesting/include/CodeCoverage.h b/Sources/CDatadogSDKTesting/include/CodeCoverage.h deleted file mode 100644 index 27826c99..00000000 --- a/Sources/CDatadogSDKTesting/include/CodeCoverage.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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 2020-Present Datadog, Inc. - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -const char* _Nonnull LLVMCoverageInfoForProfile(const char* _Nonnull prof_data, const char* _Nonnull const* _Nullable images, unsigned int image_count); - -#ifdef __cplusplus -} -#endif diff --git a/Sources/CDatadogSDKTesting/include/SymbolAddress.h b/Sources/CDatadogSDKTesting/include/SymbolAddress.h deleted file mode 100644 index a2026dfd..00000000 --- a/Sources/CDatadogSDKTesting/include/SymbolAddress.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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 2020-Present Datadog, Inc. - */ - -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void* _Nullable FindSymbolInImage(const char* _Nonnull symbol, const struct mach_header* _Nonnull image, intptr_t slide); -void ProfileResetCounters(void* _Nonnull beginCounters, void* _Nonnull endCounters, void* _Nonnull beginData, void* _Nonnull endData); - -#ifdef __cplusplus -} -#endif diff --git a/Sources/DatadogSDKTesting/Config.swift b/Sources/DatadogSDKTesting/Config.swift index 7824c523..3e8caf27 100644 --- a/Sources/DatadogSDKTesting/Config.swift +++ b/Sources/DatadogSDKTesting/Config.swift @@ -45,7 +45,7 @@ final class Config { /// Intelligent test runner related environment var gitUploadEnabled: Bool = true var itrEnabled: Bool = true - var coverageMode: CodeCoverageMode = .total + var codeCoverageEnabled: Bool = true var excludedBranches: Set = [] var codeCoveragePriority: CodeCoveragePriority = .utility @@ -128,10 +128,7 @@ final class Config { gitUploadEnabled = env[.enableCiVisibilityGitUpload] ?? gitUploadEnabled itrEnabled = env[.enableCiVisibilityITR] ?? itrEnabled excludedBranches = env[.ciVisibilityExcludedBranches] ?? excludedBranches - - let coverageEnabled = env[.enableCiVisibilityCodeCoverage] ?? itrEnabled - let coveragePerTestOnly = env[.ciVisibilityCodeCoverageOnlyPerTest] ?? false - coverageMode = coverageEnabled ? (coveragePerTestOnly ? .perTest : .total) : .disabled + codeCoverageEnabled = env[.enableCiVisibilityCodeCoverage] ?? itrEnabled /// EFD efdEnabled = env[.enableCiVisibilityEFD] ?? efdEnabled @@ -224,7 +221,7 @@ extension Config: CustomDebugStringConvertible { Disable NTP Clock: \(disableNTPClock) Disable Git Information: \(disableGitInformation) Git Upload Enabled: \(gitUploadEnabled) - Coverage: \(coverageMode) + Coverage Enabled: \(codeCoverageEnabled) ITR Enabled: \(itrEnabled) Excluded Branches: \(excludedBranches) Test Retries Enabled: \(testRetriesEnabled) diff --git a/Sources/DatadogSDKTesting/Coverage/CoverageSettings.swift b/Sources/DatadogSDKTesting/Coverage/CoverageSettings.swift index 955de44e..11bf3841 100644 --- a/Sources/DatadogSDKTesting/Coverage/CoverageSettings.swift +++ b/Sources/DatadogSDKTesting/Coverage/CoverageSettings.swift @@ -40,38 +40,3 @@ enum CodeCoveragePriority: Int, EnvironmentValue, CustomDebugStringConvertible { } } } - -enum CodeCoverageMode { - case disabled - case perTest - case total - - var isTotal: Bool { - switch self { - case .total: return true - default: return false - } - } - - var isPerTest: Bool { - switch self { - case .perTest: return true - default: return false - } - } - - var isEnabled: Bool { - switch self { - case .disabled: return false - default: return true - } - } - - var debugDescription: String { - switch self { - case .total: return "Total" - case .perTest: return "Per-Test Only" - case .disabled: return "Disabled" - } - } -} diff --git a/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift b/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift index b02f508f..d4626c37 100644 --- a/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift +++ b/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift @@ -7,44 +7,58 @@ import Foundation @_implementationOnly import EventsExporter @_implementationOnly import CDatadogSDKTesting +@_implementationOnly import CodeCoverage typealias cFunc = @convention(c) () -> Void final class DDCoverageHelper { - var llvmProfileURL: URL - var storagePath: Directory - var initialCoverageSaved: Bool - let isTotal: Bool + let collector: CoverageCollector + let exporter: EventsExporterProtocol + let workspacePath: String? + + let storagePath: Directory let debug: Bool let coverageWorkQueue: OperationQueue - init?(storagePath: Directory, total: Bool, priority: CodeCoveragePriority, debug: Bool) { - guard let profilePath = Self.profileGetFileName(), BinaryImages.profileImages.count > 0 else { - Log.print("Coverage not properly enabled in project, check documentation") - Log.debug("LLVM_PROFILE_FILE: \(Self.profileGetFileName() ?? "NIL")") - Log.debug("Profile Images count: \(BinaryImages.profileImages.count)") + init?(storagePath: Directory, exporter: EventsExporterProtocol, workspacePath: String?, priority: CodeCoveragePriority, debug: Bool) { + do { + self.collector = try CoverageCollector(for: PlatformUtils.xcodeVersion, temp: storagePath.url) + self.debug = debug + self.storagePath = storagePath + self.exporter = exporter + self.workspacePath = workspacePath + coverageWorkQueue = OperationQueue() + coverageWorkQueue.qualityOfService = priority.qos + coverageWorkQueue.maxConcurrentOperationCount = max(ProcessInfo.processInfo.activeProcessorCount - 1, 1) + setFileLimit() + } catch { + Log.print("Coverage initialisation error: \(error)") return nil } - - llvmProfileURL = URL(fileURLWithPath: profilePath) - isTotal = total - self.debug = debug - self.storagePath = storagePath - Log.debug("LLVM Coverage location: \(llvmProfileURL.path)") - Log.debug("DDCoverageHelper location: \(storagePath.url.path)") - initialCoverageSaved = false - coverageWorkQueue = OperationQueue() - coverageWorkQueue.qualityOfService = priority.qos - coverageWorkQueue.maxConcurrentOperationCount = max(ProcessInfo.processInfo.activeProcessorCount - 1, 1) - setFileLimit() } - - func clearCounters() { - BinaryImages.profileImages.forEach { - ProfileResetCounters($0.beginCountersFuncPtr, - $0.endCountersFuncPtr, - $0.beginDataFuncPtr, - $0.endCountersFuncPtr) + + func startTest() { + do { + try collector.startCoverageGathering() + } catch { + Log.debug("Can't start coverage gathering, error: \(error)") + } + } + + func endTest(testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64) { + let file: URL + do { + file = try collector.stopCoverageGathering() + } catch { + Log.debug("Coverage gathering error: \(error)") + return + } + coverageWorkQueue.addOperation { + guard FileManager.default.fileExists(atPath: file.path) else { + return + } + self.exporter.export(coverage: file, processor: self.collector.processor, workspacePath: self.workspacePath, + testSessionId: testSessionId, testSuiteId: testSuiteId, spanId: spanId) } } @@ -67,38 +81,6 @@ final class DDCoverageHelper { Log.debug("Can't increase open file limit") } } - - func setTest(name: String, testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64) { - if !self.initialCoverageSaved { - profileSetFilename(url: llvmProfileURL) - Self.internalWriteProfile() - initialCoverageSaved = true - } - let saveURL = getURLForTest(name: name, testSessionId: testSessionId, testSuiteId: testSuiteId, spanId: spanId) - profileSetFilename(url: saveURL) - } - - func getURLForTest(name: String, testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64) -> URL { - var cleanedName = name.components(separatedBy: Self.nameNotAllowed) - .filter { $0.count > 0 } - .joined(separator: "+") - if cleanedName.count > 20 { - cleanedName = "\(cleanedName.prefix(10))-\(cleanedName.suffix(10))" - } - let finalName = "\(testSessionId)__\(testSuiteId)__\(spanId)__\(cleanedName)" - return storagePath.url.appendingPathComponent(finalName).appendingPathExtension("profraw") - } - - func writeTestProfile() { - // Write first to our test file - Self.internalWriteProfile() - if isTotal { - // Switch profile to llvm original destination - profileSetFilename(url: llvmProfileURL) - // Write to llvm original destination - Self.internalWriteProfile() - } - } deinit { if !debug { @@ -112,25 +94,6 @@ final class DDCoverageHelper { try? storagePath.delete() } - private static func internalWriteProfile() { - BinaryImages.profileImages.forEach { - let llvm_profile_write_file = unsafeBitCast($0.writeFileFuncPtr, to: cFunc.self) - llvm_profile_write_file() - } - } - - private func profileSetFilename(url: URL) { - setenv("LLVM_PROFILE_FILE", url.path, 1) - BinaryImages.profileImages.forEach { - let llvm_profile_initialize_file = unsafeBitCast($0.profileInitializeFileFuncPtr, to: cFunc.self) - llvm_profile_initialize_file() - } - } - - private static func profileGetFileName() -> String? { - getenv("LLVM_PROFILE_FILE").map { String(cString: $0) } - } - fileprivate static func generateProfData(profrawFile: URL) -> URL? { let outputURL = profrawFile.deletingPathExtension().appendingPathExtension("profdata") let input = profrawFile.path @@ -147,7 +110,7 @@ final class DDCoverageHelper { guard let profDataURL = DDCoverageHelper.generateProfData(profrawFile: profrawFile) else { return nil } - let covJsonURL = profDataURL.deletingLastPathComponent().appendingPathComponent("coverageFile").appendingPathExtension("json") + let covJsonURL = profDataURL.deletingLastPathComponent().appendingPathComponent("TotalCoverage").appendingPathExtension("json") let binariesPath = binaryImagePaths.map { #""\#($0)""# }.joined(separator: " -object ") let commandToRun = #"xcrun llvm-cov export -instr-profile "\#(profDataURL.path)" \#(binariesPath) > "\#(covJsonURL.path)""# guard let llvmCovOutput = Spawn.combined(try: commandToRun, log: Log.instance) else { @@ -160,45 +123,18 @@ final class DDCoverageHelper { static func getLineCodeCoverage() -> Double? { // Check do we have profiling enabled - guard let llvmProfilePath = profileGetFileName() else { return nil } - // Save all profiling data - internalWriteProfile() + guard let llvmProfilePath = try? CoverageCollector.currentCoverageFile else { return nil } + let binaries = CoveredBinary.currentProcessBinaries + // if not continuous mode then save all data + if !llvmProfilePath.contains(exactWord: "%c") { + // Save all profiling data + binaries.forEach { $0.write() } + } // Locate profraw file - let profileFolder = URL(fileURLWithPath: llvmProfilePath).deletingLastPathComponent() - guard let file = FileManager.default - .enumerator(at: profileFolder, includingPropertiesForKeys: nil, options: .skipsSubdirectoryDescendants) - .flatMap({ $0.first { ($0 as? URL)?.pathExtension == "profraw" } }) - .map({ $0 as! URL }) - else { return nil } - // get coverage - let images = BinaryImages.binaryImagesPath - let coverage = DDCoverageHelper.getModuleCoverage(profrawFile: file, binaryImagePaths: images) + guard let llvmProfileUrl = CoverageCollector.currentCoverageFileURL else { return nil } + // Get total coverage + let coverage = DDCoverageHelper.getModuleCoverage(profrawFile: llvmProfileUrl, + binaryImagePaths: binaries.map { $0.path }) return coverage?.data.first?.totals.lines.percent } - - static func load() -> Bool { - guard let llvmProfilePath = profileGetFileName() else { return false } - Log.debug("DDCoverageHelper patching environment: \(llvmProfilePath)") - var newEnv = llvmProfilePath.replacingOccurrences(of: "%c", with: "") - if newEnv.range(of: "%m") == nil { - newEnv = newEnv.replacingOccurrences(of: ".profraw", with: "%m.profraw") - } - setenv("LLVM_PROFILE_FILE", newEnv, 1) - if newEnv != llvmProfilePath { - BinaryImages.profileImages.forEach { - let set_page_size = unsafeBitCast($0.setPageSizeFuncPtr, - to: (@convention(c) (UInt) -> Void).self) - set_page_size(0) - } - BinaryImages.profileImages.forEach { - let llvm_profile_initialize_file = unsafeBitCast($0.profileInitializeFileFuncPtr, to: cFunc.self) - llvm_profile_initialize_file() - } - } - Log.debug("DDCoverageHelper patched environment") - return true - } - - private static let nameNotAllowed: CharacterSet = .alphanumerics.union(.init(charactersIn: "-._")).inverted - private static var continuousModeEnabled: Bool = false } diff --git a/Sources/DatadogSDKTesting/DDTest.swift b/Sources/DatadogSDKTesting/DDTest.swift index f1fa1fc9..cc0467ce 100644 --- a/Sources/DatadogSDKTesting/DDTest.swift +++ b/Sources/DatadogSDKTesting/DDTest.swift @@ -90,11 +90,7 @@ public class DDTest: NSObject { } if !itr.skipped, let coverageHelper = DDTestMonitor.instance?.coverageHelper { - coverageHelper.setTest(name: name, - testSessionId: module.sessionId.rawValue, - testSuiteId: suite.id.rawValue, - spanId: span.context.spanId.rawValue) - coverageHelper.clearCounters() + coverageHelper.startTest() } } @@ -257,17 +253,9 @@ extension DDTest { } if !itr.skipped, let coverageHelper = DDTestMonitor.instance?.coverageHelper { - coverageHelper.writeTestProfile() - let testSessionId = module.sessionId.rawValue - let testSuiteId = suite.id.rawValue - let spanId = span.context.spanId.rawValue - let coverageFileURL = coverageHelper.getURLForTest(name: name, testSessionId: testSessionId, testSuiteId: testSuiteId, spanId: spanId) - coverageHelper.coverageWorkQueue.addOperation { - guard FileManager.default.fileExists(atPath: coverageFileURL.path) else { - return - } - DDTestMonitor.tracer.eventsExporter?.export(coverage: coverageFileURL, testSessionId: testSessionId, testSuiteId: testSuiteId, spanId: spanId, workspacePath: DDTestMonitor.env.workspacePath, binaryImagePaths: BinaryImages.binaryImagesPath) - } + coverageHelper.endTest(testSessionId: module.sessionId.rawValue, + testSuiteId: suite.id.rawValue, + spanId: span.context.spanId.rawValue) } StderrCapture.syncData() diff --git a/Sources/DatadogSDKTesting/DDTestModule.swift b/Sources/DatadogSDKTesting/DDTestModule.swift index 7691f9e9..0f19e7b9 100644 --- a/Sources/DatadogSDKTesting/DDTestModule.swift +++ b/Sources/DatadogSDKTesting/DDTestModule.swift @@ -198,8 +198,8 @@ public class DDTestModule: NSObject, Encodable { if itrSkipped == 0 { meta[DDItrTags.itrSkippedTests] = "false" meta[DDTestSessionTags.testItrSkipped] = "false" - if !DDTestMonitor.config.coverageMode.isPerTest { - metrics[DDTestSessionTags.testCoverageLines] = DDCoverageHelper.getLineCodeCoverage() + if let linesCovered = DDCoverageHelper.getLineCodeCoverage() { + metrics[DDTestSessionTags.testCoverageLines] = linesCovered } } else { meta[DDItrTags.itrSkippedTests] = "true" diff --git a/Sources/DatadogSDKTesting/DDTestMonitor.swift b/Sources/DatadogSDKTesting/DDTestMonitor.swift index 997ee61a..24c3cb3e 100644 --- a/Sources/DatadogSDKTesting/DDTestMonitor.swift +++ b/Sources/DatadogSDKTesting/DDTestMonitor.swift @@ -329,7 +329,7 @@ internal class DDTestMonitor { } } - if DDTestMonitor.config.coverageMode.isEnabled { + if DDTestMonitor.config.codeCoverageEnabled { let coverageSetup = BlockOperation { [self] in guard let settings = tracerBackendConfig?.itr, settings.codeCoverage else { Log.debug("Coverage Disabled") @@ -338,12 +338,17 @@ internal class DDTestMonitor { // Activate Coverage Log.debug("Coverage Enabled") guard let temp = try? DDTestMonitor.cacheManager?.temp(feature: "coverage") else { - Log.print("Coverage init failed. Can't create temp directiry.") + Log.print("Coverage init failed. Can't create temp directory.") coverageHelper = nil return } - coverageHelper = DDCoverageHelper(storagePath: temp, - total: DDTestMonitor.config.coverageMode.isTotal, + guard let exporter = DDTestMonitor.tracer.eventsExporter else { + Log.print("Coverage init failed. Exporter is nil.") + coverageHelper = nil + return + } + coverageHelper = DDCoverageHelper(storagePath: temp, exporter: exporter, + workspacePath: DDTestMonitor.env.workspacePath, priority: DDTestMonitor.config.codeCoveragePriority, debug: DDTestMonitor.config.extraDebugCodeCoverage) } diff --git a/Sources/DatadogSDKTesting/Environment/EnvironmentKeys.swift b/Sources/DatadogSDKTesting/Environment/EnvironmentKeys.swift index 22d52db8..f7bcb8f0 100644 --- a/Sources/DatadogSDKTesting/Environment/EnvironmentKeys.swift +++ b/Sources/DatadogSDKTesting/Environment/EnvironmentKeys.swift @@ -41,7 +41,6 @@ internal enum EnvironmentKey: String, CaseIterable { case enableCiVisibilityGitUpload = "DD_CIVISIBILITY_GIT_UPLOAD_ENABLED" case enableCiVisibilityCodeCoverage = "DD_CIVISIBILITY_CODE_COVERAGE_ENABLED" case ciVisibilityCodeCoveragePriority = "DD_CIVISIBILITY_CODE_COVERAGE_PRIORITY" - case ciVisibilityCodeCoverageOnlyPerTest = "DD_CIVISIBILITY_CODE_COVERAGE_ONLY_PER_TEST" case enableCiVisibilityITR = "DD_CIVISIBILITY_ITR_ENABLED" case ciVisibilityExcludedBranches = "DD_CIVISIBILITY_EXCLUDED_BRANCHES" case ciVisibilityReportHostname = "DD_CIVISIBILITY_REPORT_HOSTNAME" diff --git a/Sources/DatadogSDKTesting/FrameworkLoadHandler.swift b/Sources/DatadogSDKTesting/FrameworkLoadHandler.swift index 96e14495..971f4f0e 100644 --- a/Sources/DatadogSDKTesting/FrameworkLoadHandler.swift +++ b/Sources/DatadogSDKTesting/FrameworkLoadHandler.swift @@ -28,14 +28,6 @@ enum FrameworkLoadHandler { } if config.isInTestMode { - // When code coverage is enabled modify profile name so it disables countinuous profiling - // or we cannot recover coverage manually - if config.coverageMode.isEnabled || config.itrEnabled { - if DDCoverageHelper.load() { - DDTestMonitor.envReader = ProcessEnvironmentReader() - } - } - if config.isTestObserverNeeded && !config.disableTestInstrumenting { testObserver = DDTestObserver() testObserver?.start() diff --git a/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift b/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift index cdc4964a..4b365ed1 100644 --- a/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift +++ b/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift @@ -5,6 +5,7 @@ */ import Foundation +import MachO @_implementationOnly import CDatadogSDKTesting /// It stores information about loaded mach images @@ -14,30 +15,15 @@ struct MachOImage { var path: String } -struct ProfileInfoImage { - let name: String - var path: String - let writeFileFuncPtr: UnsafeMutableRawPointer - let beginCountersFuncPtr: UnsafeMutableRawPointer - let endCountersFuncPtr: UnsafeMutableRawPointer - let beginDataFuncPtr: UnsafeMutableRawPointer - let endDataFuncPtr: UnsafeMutableRawPointer - let profileInitializeFileFuncPtr: UnsafeMutableRawPointer - let setPageSizeFuncPtr: UnsafeMutableRawPointer -} - struct BinaryImages { private static var instance = BinaryImages() private var imageAddresses: [String: MachOImage] - private var profileImages: [ProfileInfoImage] - private var binaryImagesPath: [String] private init() { /// User library addresses are randomized each time an app is run, create a map to locate library addresses by name, /// system libraries are not so address returned is 0 imageAddresses = [String: MachOImage]() - profileImages = [ProfileInfoImage]() let numImages = _dyld_image_count() for i in 0 ..< numImages { guard let header = _dyld_get_image_header(i) else { @@ -46,50 +32,20 @@ struct BinaryImages { let path = String(cString: _dyld_get_image_name(i)) let name = URL(fileURLWithPath: path).lastPathComponent let slide = _dyld_get_image_vmaddr_slide(i) + guard slide != 0 else { continue } + + if slide != 0 { imageAddresses[name] = MachOImage(header: header, slide: slide, path: path) } else { // Its a system library, use library Address as slide value instead of 0 imageAddresses[name] = MachOImage(header: header, slide: Int(bitPattern: header), path: path) } - - if name == "DatadogSDKTesting" { - // We dont want our own libray profiling information - continue - } - if let write_file_symbol = FindSymbolInImage("___llvm_profile_write_file", header, slide), - let begin_counters_symbol = FindSymbolInImage("___llvm_profile_begin_counters", header, slide), - let end_counters_symbol = FindSymbolInImage("___llvm_profile_end_counters", header, slide), - let begin_data_symbol = FindSymbolInImage("___llvm_profile_begin_data", header, slide), - let end_data_symbol = FindSymbolInImage("___llvm_profile_end_data", header, slide), - let profile_initialize_symbol = FindSymbolInImage("___llvm_profile_initialize", header, slide), - let set_pagesize_symbol = FindSymbolInImage("___llvm_profile_set_page_size", header, slide) - { - let profileImage = ProfileInfoImage(name: name, - path: path, - writeFileFuncPtr: write_file_symbol, - beginCountersFuncPtr: begin_counters_symbol, - endCountersFuncPtr: end_counters_symbol, - beginDataFuncPtr: begin_data_symbol, - endDataFuncPtr: end_data_symbol, - profileInitializeFileFuncPtr: profile_initialize_symbol, - setPageSizeFuncPtr: set_pagesize_symbol) - profileImages.append(profileImage) - } } - binaryImagesPath = profileImages.map { $0.path } } /// Structure to store all loaded libraries and images in the process static var imageAddresses: [String: MachOImage] = { BinaryImages.instance.imageAddresses }() - - static var profileImages: [ProfileInfoImage] = { - BinaryImages.instance.profileImages - }() - - static var binaryImagesPath: [String] = { - BinaryImages.instance.binaryImagesPath - }() } diff --git a/Sources/DatadogSDKTesting/ImageSymbols/DDSymbolicator.swift b/Sources/DatadogSDKTesting/ImageSymbols/DDSymbolicator.swift index 5226a497..a634b91a 100644 --- a/Sources/DatadogSDKTesting/ImageSymbols/DDSymbolicator.swift +++ b/Sources/DatadogSDKTesting/ImageSymbols/DDSymbolicator.swift @@ -6,7 +6,7 @@ import Foundation import MachO -@_implementationOnly import CDatadogSDKTesting +@_implementationOnly import CodeCoverage @_implementationOnly import EventsExporter enum DDSymbolicator { @@ -246,11 +246,11 @@ enum DDSymbolicator { } /// It locates the address in the image og the library where the symbol is located, it must receive a mangled name - static func address(forSymbolName name: String, library: String) -> UnsafeMutableRawPointer? { + static func address(forSymbolName name: String, library: String) -> UnsafeRawPointer? { guard let imageAddressHeader = BinaryImages.imageAddresses[library]?.header, let imageSlide = BinaryImages.imageAddresses[library]?.slide else { return nil } - let symbol = FindSymbolInImage(name, imageAddressHeader, imageSlide) + let symbol = CoveredBinary.findSymbol(named: name, image: imageAddressHeader, slide: imageSlide) return symbol } diff --git a/Sources/DatadogSDKTesting/Utils/PlatformUtils.swift b/Sources/DatadogSDKTesting/Utils/PlatformUtils.swift index 04d0081d..263c6e19 100644 --- a/Sources/DatadogSDKTesting/Utils/PlatformUtils.swift +++ b/Sources/DatadogSDKTesting/Utils/PlatformUtils.swift @@ -11,6 +11,8 @@ import Foundation import Cocoa import SystemConfiguration #endif +@_implementationOnly import CodeCoverage + struct PlatformUtils { static func getRunningPlatform() -> String { var platform: String @@ -85,7 +87,8 @@ struct PlatformUtils { if NSClassFromString("XCTest") != nil { return ("Xcode", getXcodeVersion()) } else { - return (ProcessInfo.processInfo.processName, (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "") + return (ProcessInfo.processInfo.processName, + (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "") } } @@ -167,4 +170,17 @@ struct PlatformUtils { return NSLocale.current.languageCode ?? "none" #endif } + + static var xcodeVersion: XcodeVersion { + let version = getXcodeVersion() + guard version.count >= 2 else { + return .xcode16 + } + switch version[version.startIndex...version.index(version.startIndex, offsetBy: 2)] { + case "14": return .xcode14 + case "15": return .xcode15 + case "16": return .xcode16 + default: return .xcode16 + } + } } diff --git a/Sources/EventsExporter/CoverageExporter/CoverageExporter.swift b/Sources/EventsExporter/CoverageExporter/CoverageExporter.swift index 5c13b267..ede3a27e 100644 --- a/Sources/EventsExporter/CoverageExporter/CoverageExporter.swift +++ b/Sources/EventsExporter/CoverageExporter/CoverageExporter.swift @@ -6,6 +6,7 @@ import Foundation import OpenTelemetrySdk +import CodeCoverage internal class CoverageExporter { let coverageDirectory = "com.datadog.civisibility/coverage/v1" @@ -21,8 +22,16 @@ internal class CoverageExporter { performance: PerformancePreset.instantDataDelivery, dateProvider: SystemDateProvider() ) + + let prefix = """ + { + "version": 2, + "coverages": [ + """ + + let suffix = "]\n}" - let dataFormat = DataFormat(prefix: "", suffix: "", separator: "\n") + let dataFormat = DataFormat(prefix: prefix, suffix: suffix, separator: ",") let coverageFileWriter = FileWriter( dataFormat: dataFormat, @@ -57,41 +66,43 @@ internal class CoverageExporter { requestBuilder.addFieldsCallback = addCoverage } - func exportCoverage(coverage: URL, testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64, workspacePath: String?, binaryImagePaths: [String]) { + func exportCoverage(coverage: URL, processor: CoverageProcessor, workspacePath: String?, + testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64) + { Log.debug("Start processing coverage: \(coverage.path)") - let profData: URL - do { - profData = try DDCoverageConversor.generateProfData(profrawFile: coverage) - } catch { - Log.print("Profiler Data generation failed: \(error)") - return + var coverageData: TestCodeCoverage? = nil + + defer { + if configuration.debug.saveCodeCoverageFiles { + if let coverageData = coverageData, let data = try? JSONEncoder.default().encode(coverageData) { + let testName = coverage.deletingPathExtension().lastPathComponent.components(separatedBy: "__").last! + let jsonURL = coverage.deletingLastPathComponent().appendingPathComponent(testName).appendingPathExtension("json") + try? data.write(to: jsonURL) + } + } else { + try? FileManager.default.removeItem(at: coverage) + } } - let ddCoverage = DDCoverageConversor.getDatadogCoverage(profdataFile: profData, testSessionId: testSessionId, - testSuiteId: testSuiteId, spanId: spanId, workspacePath: workspacePath, - binaryImagePaths: binaryImagePaths) - - if configuration.debug.saveCodeCoverageFiles { - let data = try? JSONEncoder.default().encode(ddCoverage) - let testName = coverage.deletingPathExtension().lastPathComponent.components(separatedBy: "__").last! - let jsonURL = coverage.deletingLastPathComponent().appendingPathComponent(testName).appendingPathExtension("json") - try? data?.write(to: jsonURL) - } else { - try? FileManager.default.removeItem(at: coverage) - try? FileManager.default.removeItem(at: profData) + switch processor.filesCovered(in: coverage) { + case .failure(let error): + Log.print("Code coverage generation failed: \(error)") + return + case .success(let info): + coverageData = TestCodeCoverage(sessionId: testSessionId, + suiteId: testSuiteId, + spanId: spanId, + workspace: workspacePath, + files: info.files.values) + coverageStorage.writer.write(value: coverageData!) } - coverageStorage.writer.write(value: ddCoverage) Log.debug("End processing coverage: \(coverage.path)") } private func addCoverage(request: MultipartFormDataRequest, data: Data?) { - guard let data = data, - let newline = Character("\n").asciiValue else { return } - - let separatedData = data.split(separator: newline) - separatedData.enumerated().forEach { - request.addDataField(named: "coverage\($0)", data: $1, mimeType: .applicationJSON) - } + guard let data = data else { return } + request.addDataField(named: "coverage", data: data, mimeType: .applicationJSON) request.addDataField(named: "event", data: #"{"dummy": true}"#.data(using: .utf8)!, mimeType: .applicationJSON) } } + diff --git a/Sources/EventsExporter/CoverageExporter/DDCoverageConversor.swift b/Sources/EventsExporter/CoverageExporter/DDCoverageConversor.swift deleted file mode 100644 index 191dd94d..00000000 --- a/Sources/EventsExporter/CoverageExporter/DDCoverageConversor.swift +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 2020-Present Datadog, Inc. - */ - -import Foundation -import CDatadogSDKTesting - -struct DDCoverageConversor { - static func generateProfData(profrawFile: URL) throws -> URL { - let outputURL = profrawFile.deletingPathExtension().appendingPathExtension("profdata") - let input = profrawFile.path - let outputPath = outputURL.path - let commandToRun = #"xcrun llvm-profdata merge -sparse "\#(input)" -o "\#(outputPath)""# - try Spawn.command(commandToRun) - return outputURL - } - - static func getDatadogCoverage(profdataFile: URL, testSessionId: UInt64, testSuiteId: UInt64, - spanId: UInt64, workspacePath: String?, binaryImagePaths: [String]) -> DDCoverageFormat? { -#if swift(>=5.3) - // LLVM Support is dependant on binary target, swift 5.3 is needed - - let json = binaryImagePaths.withCStringsArray { images in - LLVMCoverageInfoForProfile(profdataFile.path, images, UInt32(images.count)) - } - - let jsonStr = String(cString: json) - json.deallocate() - - guard let llvmCov = LLVMSimpleCoverageFormat(jsonStr) else { - return nil - } - - var datadogCoverage = DDCoverageFormat() - datadogCoverage.addCoverage(llvmFormat: llvmCov, testSessionId: testSessionId, - testSuiteId: testSuiteId, spanId: spanId, - workspacePath: workspacePath) - - if datadogCoverage.coverages.count > 0 { - return datadogCoverage - } else { - return nil - } -#else - return nil -#endif - } -} diff --git a/Sources/EventsExporter/CoverageExporter/DDCoverageFormat.swift b/Sources/EventsExporter/CoverageExporter/DDCoverageFormat.swift deleted file mode 100644 index 5feab91d..00000000 --- a/Sources/EventsExporter/CoverageExporter/DDCoverageFormat.swift +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 2020-Present Datadog, Inc. - */ - -import Foundation - -struct DDCoverageFormat: Encodable { - var version: Int = 2 - var coverages = [Coverage]() - - struct Coverage: Encodable { - var test_session_id: UInt64 - var test_suite_id: UInt64 - var span_id: UInt64 - var files = [File]() - - struct File: Encodable { - let filename: String - let segments: [Segment] - } - - struct Segment: Encodable { - var startLine = 0 - var startColumn = 0 - var endLine = 0 - var endColumn = 0 - var count = 0 - - func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(startLine) - try container.encode(startColumn) - try container.encode(endLine) - try container.encode(endColumn) - try container.encode(count) - } - - init() {} - } - - init?(llvmFormat: LLVMSimpleCoverageFormat, testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64, workspacePath: String?) { - guard let llvmFiles = llvmFormat.data.first?.files else { return nil } - - self.test_session_id = testSessionId - self.test_suite_id = testSuiteId - self.span_id = spanId - - for llvmFile in llvmFiles { - var segments = [Segment]() - var currentSegment = Segment() - var previousCount = 0 - for segment in llvmFile.segments { - let line = segment.line - let column = segment.column - let count = segment.count - if previousCount == 0 { - if count == 0 { - continue - } else { - // start Boundary - currentSegment.startLine = line - currentSegment.startColumn = column - currentSegment.count = count - previousCount = count - } - } else { - if count == 0 { - // end Segment - currentSegment.endLine = line - currentSegment.endColumn = column - segments.append(currentSegment) - currentSegment = Segment() - previousCount = 0 - } else if count == previousCount { - // continue boundary - } else { - // change Segment - if column > 0 { - currentSegment.endLine = line - currentSegment.endColumn = column - 1 - } else { - currentSegment.endLine = line - 1 - currentSegment.endColumn = column - } - segments.append(currentSegment) - currentSegment = Segment() - currentSegment.startLine = line - currentSegment.startColumn = column - currentSegment.count = count - previousCount = count - } - } - } - if segments.count > 0 { - var filename = llvmFile.filename - if let workspacePath = workspacePath { - filename = filename.replacingOccurrences(of: workspacePath + "/", with: "") - } - let file = File(filename: filename, segments: segments) - files.append(file) - } - } - } - - } - - mutating func addCoverage(llvmFormat: LLVMSimpleCoverageFormat, testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64, workspacePath: String?) { - guard let coverage = Coverage(llvmFormat: llvmFormat, testSessionId: testSessionId, testSuiteId: testSuiteId, spanId: spanId, workspacePath: workspacePath) else { - return - } - coverages.append(coverage) - } - - -} diff --git a/Sources/EventsExporter/CoverageExporter/TestCodeCoverage.swift b/Sources/EventsExporter/CoverageExporter/TestCodeCoverage.swift new file mode 100644 index 00000000..b8975197 --- /dev/null +++ b/Sources/EventsExporter/CoverageExporter/TestCodeCoverage.swift @@ -0,0 +1,76 @@ +/* + * 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 2020-Present Datadog, Inc. + */ + +import Foundation +import CodeCoverage + +struct TestCodeCoverage: Encodable { + let sessionId: UInt64 + let suiteId: UInt64 + let spanId: UInt64 + let files: [File] + + struct File: Encodable { + let name: String + let bitmap: Data + + enum CodingKeys: String, CodingKey { + case name = "filename" + case bitmap + } + + init(info: CoverageInfo.File, workspace: String?) { + if let workspace = workspace, info.name.count >= workspace.count { + self.name = info.name.replacingOccurrences( + of: workspace, with: "", + range: info.name.startIndex...Values) { + self.sessionId = sessionId + self.suiteId = suiteId + self.spanId = spanId + let workspacePath = workspace.map { $0.last == "/" ? $0 : $0 + "/" } + self.files = files.map { File(info: $0, workspace: workspacePath) } + } +} + +extension CoverageInfo.File { + var coveredLines: IndexSet { + var indexes = IndexSet() + for location in segments.keys { + indexes.insert(integersIn: Int(location.startLine)...Int(location.endLine)) + } + return indexes + } +} diff --git a/Sources/EventsExporter/EventsExporter.swift b/Sources/EventsExporter/EventsExporter.swift index 9e064d10..f7200171 100644 --- a/Sources/EventsExporter/EventsExporter.swift +++ b/Sources/EventsExporter/EventsExporter.swift @@ -6,13 +6,15 @@ import Foundation import OpenTelemetrySdk +import CodeCoverage public protocol EventsExporterProtocol: SpanExporter { var endpointURLs: Set { get } func exportEvent(event: T) func searchCommits(repositoryURL: String, commits: [String]) -> [String] - func export(coverage: URL, testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64, workspacePath: String?, binaryImagePaths: [String]) + func export(coverage: URL, processor: CoverageProcessor, workspacePath: String?, + testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64) func uploadPackFiles(packFilesDirectory: Directory, commit: String, repository: String) throws func skippableTests(repositoryURL: String, sha: String, testLevel: ITRTestLevel, configurations: [String: String], customConfigurations: [String: String]) -> SkipTests? @@ -81,8 +83,11 @@ public class EventsExporter: EventsExporterProtocol { return .success } - public func export(coverage: URL, testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64, workspacePath: String?, binaryImagePaths: [String]) { - coverageExporter.exportCoverage(coverage: coverage, testSessionId: testSessionId, testSuiteId: testSuiteId, spanId: spanId, workspacePath: workspacePath, binaryImagePaths: binaryImagePaths) + public func export(coverage: URL, processor: CoverageProcessor, workspacePath: String?, + testSessionId: UInt64, testSuiteId: UInt64, spanId: UInt64) + { + coverageExporter.exportCoverage(coverage: coverage, processor: processor, workspacePath: workspacePath, + testSessionId: testSessionId, testSuiteId: testSuiteId, spanId: spanId) } public func searchCommits(repositoryURL: String, commits: [String]) -> [String] { diff --git a/Sources/EventsExporter/Utils/Spawn.swift b/Sources/EventsExporter/Utils/Spawn.swift index ff7be72d..8c579871 100644 --- a/Sources/EventsExporter/Utils/Spawn.swift +++ b/Sources/EventsExporter/Utils/Spawn.swift @@ -140,9 +140,9 @@ extension Spawn { return try combined(command) } catch { if debug { - Log.debug("Command \(command) failed with error \(error)") + log.debug("Command \(command) failed with error \(error)") } else { - Log.print("Command \(command) failed with error \(error)") + log.print("Command \(command) failed with error \(error)") } return nil } @@ -161,9 +161,9 @@ extension Spawn { return try output(command) } catch { if debug { - Log.debug("Command \(command) failed with error \(error)") + log.debug("Command \(command) failed with error \(error)") } else { - Log.print("Command \(command) failed with error \(error)") + log.print("Command \(command) failed with error \(error)") } return nil } @@ -178,9 +178,9 @@ extension Spawn { err!.trimmingCharacters(in: .whitespacesAndNewlines)) } catch { if debug { - Log.debug("Command \(command) failed with error \(error)") + log.debug("Command \(command) failed with error \(error)") } else { - Log.print("Command \(command) failed with error \(error)") + log.print("Command \(command) failed with error \(error)") } return nil } diff --git a/Tests/DatadogSDKTesting/EarlyFlakeDetectionTests.swift b/Tests/DatadogSDKTesting/EarlyFlakeDetectionTests.swift index a7d7d981..7b582405 100644 --- a/Tests/DatadogSDKTesting/EarlyFlakeDetectionTests.swift +++ b/Tests/DatadogSDKTesting/EarlyFlakeDetectionTests.swift @@ -8,6 +8,7 @@ @testable import EventsExporter import OpenTelemetryApi import OpenTelemetrySdk +import CodeCoverage import XCTest final class EarlyFlakeDetectionTests: XCTestCase { @@ -269,8 +270,9 @@ private extension EarlyFlakeDetectionTests { events.append(event) } - func export(coverage: URL, testSessionId: UInt64, testSuiteId: UInt64, - spanId: UInt64, workspacePath: String?, binaryImagePaths: [String]) {} + func export(coverage: URL, processor: CodeCoverage.CoverageProcessor, + workspacePath: String?, testSessionId: UInt64, + testSuiteId: UInt64, spanId: UInt64) {} func shutdown(explicitTimeout: TimeInterval?) { } diff --git a/Tests/EventsExporter/CodeCoverageModelTests.swift b/Tests/EventsExporter/CodeCoverageModelTests.swift new file mode 100644 index 00000000..5bc295a8 --- /dev/null +++ b/Tests/EventsExporter/CodeCoverageModelTests.swift @@ -0,0 +1,78 @@ +// +// CodeCoverageModelTests.swift +// EventsExporterTests +// +// Created by Yehor Popovych on 12/12/2024. +// + +import XCTest +import CodeCoverage +@testable import EventsExporter + +final class CodeCoverageModelTests: XCTestCase { + func testBitmapGeneration() throws { + let testJson = #""" + { "files": { + "Sources/OpenTelemetrySdk/Trace/SpanBuilderSdk.swift": { + "segments": [ + {"endColumn":16,"startColumn":32,"endLine":117,"startLine":108}, + {"location":{"startLine":108,"startColumn":32,"endLine":117,"endColumn":16},"count":1}, + {"startColumn":5,"endColumn":6,"startLine":42,"endLine":48}, + {"location":{"endLine":48,"startLine":42,"endColumn":6,"startColumn":5},"count":1}, + {"endLine":31,"endColumn":49,"startColumn":48,"startLine":31}, + {"location":{"endColumn":49,"startColumn":48,"startLine":31,"endLine":31},"count":1}, + {"endColumn":40,"endLine":134,"startLine":120,"startColumn":10}, + {"count":1,"location":{"startColumn":10,"endLine":134,"startLine":120,"endColumn":40}}, + {"endColumn":42,"endLine":30,"startColumn":25,"startLine":30}, + {"location":{"startColumn":25,"endColumn":42,"endLine":30,"startLine":30},"count":1}, + {"endColumn":6,"endLine":154,"startLine":136,"startColumn":10}, + {"location":{"startColumn":10,"endLine":154,"startLine":136,"endColumn":6},"count":1}, + {"startColumn":30,"endLine":159,"endColumn":26,"startLine":156}, + {"count":1,"location":{"endLine":159,"endColumn":26,"startLine":156,"startColumn":30}}, + {"endLine":32,"startLine":32,"endColumn":54,"startColumn":42}, + {"count":1,"location":{"startColumn":42,"endColumn":54,"startLine":32,"endLine":32}}, + {"startLine":28,"startColumn":28,"endLine":28,"endColumn":45}, + {"location":{"startLine":28,"endLine":28,"endColumn":45,"startColumn":28},"count":1}, + {"endLine":163,"startColumn":10,"startLine":161,"endColumn":6}, + {"count":1,"location":{"endColumn":6,"endLine":163,"startColumn":10,"startLine":161}}, + {"startColumn":41,"startLine":210,"endLine":214,"endColumn":6}, + {"count":1,"location":{"startLine":210,"endLine":214,"endColumn":6,"startColumn":41}}, + {"startLine":208,"endLine":209,"endColumn":9,"startColumn":52}, + {"count":1,"location":{"endLine":209,"endColumn":9,"startLine":208,"startColumn":52}}, + {"startColumn":72,"endColumn":10,"endLine":193,"startLine":190}, + {"location":{"startLine":190,"endColumn":10,"startColumn":72,"endLine":193},"count":1}, + {"startLine":204,"startColumn":32,"endColumn":9,"endLine":207}, + {"count":1,"location":{"endLine":207,"startColumn":32,"endColumn":9,"startLine":204}}, + {"endLine":203,"endColumn":9,"startLine":198,"startColumn":126}, + {"location":{"startLine":198,"endLine":203,"startColumn":126,"endColumn":9},"count":1}, + {"startLine":216,"startColumn":95,"endLine":220,"endColumn":9}, + {"count":1,"location":{"endColumn":9,"startLine":216,"startColumn":95,"endLine":220}}, + {"endColumn":44,"startLine":34,"startColumn":39,"endLine":34}, + {"location":{"startColumn":39,"endLine":34,"startLine":34,"endColumn":44},"count":1}, + {"endLine":222,"endColumn":9,"startLine":221,"startColumn":34}, + {"count":1,"location":{"startColumn":34,"startLine":221,"endLine":222,"endColumn":9}}, + {"endLine":225,"startLine":223,"startColumn":23,"endColumn":6}, + {"location":{"endLine":225,"endColumn":6,"startColumn":23,"startLine":223},"count":1} + ], + "name":"Sources/OpenTelemetrySdk/Trace/SpanBuilderSdk.swift" + } } + } + """# + + let info = try JSONDecoder().decode(CoverageInfo.self, from: testJson.utf8Data) + let lines = info.files.values.first!.coveredLines + let converted = TestCodeCoverage(sessionId: 0, suiteId: 0, spanId: 0, workspace: nil, files: info.files.values) + + let bitmapLines = converted.files.first!.bitmap.enumerated().reduce(into: IndexSet()) { (indexes, current) in + let (index, byte) = current + for bit in 1...8 { + if (byte >> (8 - bit)) & 1 != 0 { + let line = index * 8 + bit + indexes.insert(line) + } + } + } + + XCTAssertEqual(lines, bitmapLines) + } +} From 115c3847114fd997667e44d409d78011cea01aa7 Mon Sep 17 00:00:00 2001 From: Yehor Popovych Date: Fri, 13 Dec 2024 16:50:17 +0000 Subject: [PATCH 3/4] some binary images could be ignored. fixed optimised URL calls to avoid unneeded FS calls --- IntegrationTests/App/main.swift | 2 +- .../Coverage/DDCoverageHelper.swift | 4 ++- .../DatadogSDKTesting/Crashes/DDCrashes.swift | 6 +++-- Sources/DatadogSDKTesting/DDTestModule.swift | 4 ++- .../Environment/Environment.swift | 2 +- .../ImageSymbols/BinaryImages.swift | 10 +++----- .../ImageSymbols/DDSymbolicator.swift | 25 +++++++++++-------- .../IntelligentTestRunner/GitUploader.swift | 4 +-- .../DatadogSDKTesting/Utils/CodeOwners.swift | 14 ++++++++--- Sources/DatadogSDKTesting/Utils/GitInfo.swift | 19 ++++++++------ .../CoverageExporter/CoverageExporter.swift | 3 ++- Sources/EventsExporter/Utils/Spawn.swift | 14 ++++++++--- .../Helpers/TestsDirectory.swift | 3 ++- 13 files changed, 69 insertions(+), 41 deletions(-) diff --git a/IntegrationTests/App/main.swift b/IntegrationTests/App/main.swift index 826d4a1e..30689aac 100644 --- a/IntegrationTests/App/main.swift +++ b/IntegrationTests/App/main.swift @@ -13,7 +13,7 @@ guard let outputPath = ProcessInfo.processInfo.environment["TEST_OUTPUT_FILE"] e } // Create a exporter to export the spans to the desired file -let exporter = FileTraceExporter(outputURL: URL(fileURLWithPath: outputPath)) +let exporter = FileTraceExporter(outputURL: URL(fileURLWithPath: outputPath, isDirectory: false)) if let testClass = ProcessInfo.processInfo.environment["TEST_CLASS"], let theClass: AnyClass = Bundle.main.classNamed(testClass) diff --git a/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift b/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift index d4626c37..cefa78e3 100644 --- a/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift +++ b/Sources/DatadogSDKTesting/Coverage/DDCoverageHelper.swift @@ -110,7 +110,9 @@ final class DDCoverageHelper { guard let profDataURL = DDCoverageHelper.generateProfData(profrawFile: profrawFile) else { return nil } - let covJsonURL = profDataURL.deletingLastPathComponent().appendingPathComponent("TotalCoverage").appendingPathExtension("json") + let covJsonURL = profDataURL + .deletingLastPathComponent() + .appendingPathComponent("TotalCoverage.json", isDirectory: false) let binariesPath = binaryImagePaths.map { #""\#($0)""# }.joined(separator: " -object ") let commandToRun = #"xcrun llvm-cov export -instr-profile "\#(profDataURL.path)" \#(binariesPath) > "\#(covJsonURL.path)""# guard let llvmCovOutput = Spawn.combined(try: commandToRun, log: Log.instance) else { diff --git a/Sources/DatadogSDKTesting/Crashes/DDCrashes.swift b/Sources/DatadogSDKTesting/Crashes/DDCrashes.swift index f0c31a9a..b2d849c7 100644 --- a/Sources/DatadogSDKTesting/Crashes/DDCrashes.swift +++ b/Sources/DatadogSDKTesting/Crashes/DDCrashes.swift @@ -43,8 +43,10 @@ internal enum DDCrashes { return } - let reportURL = URL(fileURLWithPath: plCrashReporter.crashReportPath()) - sanitizerURL = reportURL.deletingLastPathComponent().appendingPathComponent("SanitizerLog") + let reportURL = URL(fileURLWithPath: plCrashReporter.crashReportPath(), isDirectory: true) + sanitizerURL = reportURL + .deletingLastPathComponent() + .appendingPathComponent("Sanitizer.log", isDirectory: false) var callback = PLCrashReporterCallbacks(version: 0, context: nil, handleSignal: signalCallback) plCrashReporter.setCrash(&callback) diff --git a/Sources/DatadogSDKTesting/DDTestModule.swift b/Sources/DatadogSDKTesting/DDTestModule.swift index 0f19e7b9..63ddaf27 100644 --- a/Sources/DatadogSDKTesting/DDTestModule.swift +++ b/Sources/DatadogSDKTesting/DDTestModule.swift @@ -75,7 +75,9 @@ public class DDTestModule: NSObject, Encodable { DDTestMonitor.instance?.instrumentationWorkQueue.addOperation { if let workspacePath = DDTestMonitor.env.workspacePath { Log.measure(name: "createCodeOwners") { - DDTestModule.codeOwners = CodeOwners(workspacePath: URL(fileURLWithPath: workspacePath)) + DDTestModule.codeOwners = CodeOwners( + workspacePath: URL(fileURLWithPath: workspacePath, isDirectory: true) + ) } } } diff --git a/Sources/DatadogSDKTesting/Environment/Environment.swift b/Sources/DatadogSDKTesting/Environment/Environment.swift index 88b7cc16..f5225054 100644 --- a/Sources/DatadogSDKTesting/Environment/Environment.swift +++ b/Sources/DatadogSDKTesting/Environment/Environment.swift @@ -201,7 +201,7 @@ internal final class Environment { } static func gitInfoAt(startingPath: String) -> GitInfo? { - var rootFolder = NSString(string: URL(fileURLWithPath: startingPath).path) + var rootFolder = NSString(string: URL(fileURLWithPath: startingPath, isDirectory: true).path) while !FileManager.default.fileExists(atPath: rootFolder.appendingPathComponent(".git")) { if rootFolder.isEqual(to: rootFolder.deletingLastPathComponent) { // We reached to the top diff --git a/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift b/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift index 4b365ed1..66eb20f4 100644 --- a/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift +++ b/Sources/DatadogSDKTesting/ImageSymbols/BinaryImages.swift @@ -10,9 +10,9 @@ import MachO /// It stores information about loaded mach images struct MachOImage { - var header: UnsafePointer? + var header: UnsafePointer var slide: Int - var path: String + var path: URL } struct BinaryImages { @@ -29,11 +29,9 @@ struct BinaryImages { guard let header = _dyld_get_image_header(i) else { continue } - let path = String(cString: _dyld_get_image_name(i)) - let name = URL(fileURLWithPath: path).lastPathComponent + let path = URL(fileURLWithPath: String(cString: _dyld_get_image_name(i)), isDirectory: false) + let name = path.lastPathComponent let slide = _dyld_get_image_vmaddr_slide(i) - guard slide != 0 else { continue } - if slide != 0 { imageAddresses[name] = MachOImage(header: header, slide: slide, path: path) diff --git a/Sources/DatadogSDKTesting/ImageSymbols/DDSymbolicator.swift b/Sources/DatadogSDKTesting/ImageSymbols/DDSymbolicator.swift index a634b91a..8d8eeec8 100644 --- a/Sources/DatadogSDKTesting/ImageSymbols/DDSymbolicator.swift +++ b/Sources/DatadogSDKTesting/ImageSymbols/DDSymbolicator.swift @@ -29,7 +29,7 @@ enum DDSymbolicator { let libraryPaths = configurationBuildPath.components(separatedBy: ":") libraryPaths.forEach { path in Log.debug("DSYMFILE enumerating: \(path)") - let buildFolder = URL(fileURLWithPath: path) + let buildFolder = URL(fileURLWithPath: path, isDirectory: true) if let dSYMFilesEnumerator = fileManager.enumerator(at: buildFolder, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles], errorHandler: { url, error -> Bool in @@ -71,7 +71,10 @@ enum DDSymbolicator { i = 0 while i < dSYMFiles.count { let dsym = dSYMFiles[i] - let dwarfFolder = dsym.appendingPathComponent("Contents").appendingPathComponent("Resources").appendingPathComponent("DWARF") + let dwarfFolder = dsym + .appendingPathComponent("Contents", isDirectory: true) + .appendingPathComponent("Resources", isDirectory: true) + .appendingPathComponent("DWARF", isDirectory: true) guard let binaries = try? FileManager.default.contentsOfDirectory(at: dwarfFolder, includingPropertiesForKeys: nil, options: []), binaries.count == 1 else { dSYMFiles.remove(at: i) continue @@ -110,8 +113,10 @@ enum DDSymbolicator { let library = String(line[libraryRange]) let libraryAddress = String(line[libraryAddressRange]) let callAddress = String(line[callAddressRange]) + + let objectURL = dSYMFiles.first(where: { $0.lastPathComponent == library }) ?? BinaryImages.imageAddresses[library]?.path - if let objectPath = dSYMFiles.first(where: { $0.lastPathComponent == library })?.path ?? BinaryImages.imageAddresses[library]?.path { + if let objectPath = objectURL?.path { let symbol = symbolWithAtos(objectPath: objectPath, libraryAdress: libraryAddress, callAddress: callAddress) if !symbol.isEmpty { linesLock.lock() @@ -149,16 +154,16 @@ enum DDSymbolicator { /// Generates a dSYM symbol file from a binary if possible /// and adds it to the dSYMFiles for the future static func generateDSYMFile(forImageName imageName: String) -> String? { - guard let binaryPath = BinaryImages.imageAddresses[imageName]?.path else { + guard let binaryURL = BinaryImages.imageAddresses[imageName]?.path else { return nil } - let binaryURL = URL(fileURLWithPath: binaryPath) - let dSYMFileURL = dsymFilesPath.appendingPathComponent(binaryURL.lastPathComponent) + let dSYMFileURL = dsymFilesPath + .appendingPathComponent(binaryURL.lastPathComponent, isDirectory: false) do { - try Spawn.command("/usr/bin/dsymutil --flat \"\(binaryPath)\" --out \"\(dSYMFileURL.path)\"") + try Spawn.command("/usr/bin/dsymutil --flat \"\(binaryURL.path)\" --out \"\(dSYMFileURL.path)\"") } catch { - Log.debug("DSYM \(binaryPath) generation failed \(error)") + Log.debug("DSYM \(binaryURL.path) generation failed \(error)") return nil } @@ -274,7 +279,7 @@ enum DDSymbolicator { static func atosSymbol(forAddress callAddress: String, library: String) -> String? { guard let imageAddress = BinaryImages.imageAddresses[library] else { return nil } - let imagePath = dSYMFiles.first(where: { $0.lastPathComponent == library })?.path ?? imageAddress.path + let imagePath = dSYMFiles.first(where: { $0.lastPathComponent == library })?.path ?? imageAddress.path.path let librarySlide = String(format: "%016llx", imageAddress.slide) @@ -298,7 +303,7 @@ enum DDSymbolicator { guard let imagePath = dSYMFiles.first(where: { $0.lastPathComponent == library })?.path else { return nil } - let symbolsOutputURL = dsymFilesPath.appendingPathComponent("\(library).symbols") + let symbolsOutputURL = dsymFilesPath.appendingPathComponent("\(library).symbols", isDirectory: false) do { try Spawn.command("/usr/bin/symbols -fullSourcePath -lazy \"\(imagePath)\"", output: symbolsOutputURL) } catch { diff --git a/Sources/DatadogSDKTesting/IntelligentTestRunner/GitUploader.swift b/Sources/DatadogSDKTesting/IntelligentTestRunner/GitUploader.swift index fb0d493c..675b6414 100644 --- a/Sources/DatadogSDKTesting/IntelligentTestRunner/GitUploader.swift +++ b/Sources/DatadogSDKTesting/IntelligentTestRunner/GitUploader.swift @@ -237,14 +237,14 @@ final class GitUploader { let commitList = commits.joined(separator: "\n") if generate(workspacePath, uploadPackfileDirectory, commitList) { - return Directory(url: URL(fileURLWithPath: uploadPackfileDirectory)) + return Directory(url: URL(fileURLWithPath: uploadPackfileDirectory, isDirectory: true)) } log.debug("Can't write packfile to cache path. Trying to workspace...") uploadPackfileDirectory = workspacePath + "/" + UUID().uuidString if generate(workspacePath, uploadPackfileDirectory, commitList) { - return Directory(url: URL(fileURLWithPath: uploadPackfileDirectory)) + return Directory(url: URL(fileURLWithPath: uploadPackfileDirectory, isDirectory: true)) } log.debug("packfile generation failed") diff --git a/Sources/DatadogSDKTesting/Utils/CodeOwners.swift b/Sources/DatadogSDKTesting/Utils/CodeOwners.swift index d2e34dfc..e65a2138 100644 --- a/Sources/DatadogSDKTesting/Utils/CodeOwners.swift +++ b/Sources/DatadogSDKTesting/Utils/CodeOwners.swift @@ -17,10 +17,16 @@ struct CodeOwners { init?(workspacePath: URL) { // Search on the possible locations CODEOWNER can exist let locations = [ - workspacePath.appendingPathComponent("CODEOWNERS"), - workspacePath.appendingPathComponent(".github").appendingPathComponent("CODEOWNERS"), - workspacePath.appendingPathComponent(".gitlab").appendingPathComponent("CODEOWNERS"), - workspacePath.appendingPathComponent(".docs").appendingPathComponent("CODEOWNERS") + workspacePath.appendingPathComponent("CODEOWNERS", isDirectory: false), + workspacePath + .appendingPathComponent(".github", isDirectory: true) + .appendingPathComponent("CODEOWNERS", isDirectory: false), + workspacePath + .appendingPathComponent(".gitlab", isDirectory: true) + .appendingPathComponent("CODEOWNERS", isDirectory: false), + workspacePath + .appendingPathComponent(".docs", isDirectory: true) + .appendingPathComponent("CODEOWNERS", isDirectory: false) ] let fm = FileManager.default guard let location = locations.first(where: { fm.fileExists(atPath: $0.path) }) else { diff --git a/Sources/DatadogSDKTesting/Utils/GitInfo.swift b/Sources/DatadogSDKTesting/Utils/GitInfo.swift index 5446aeb2..69f22778 100644 --- a/Sources/DatadogSDKTesting/Utils/GitInfo.swift +++ b/Sources/DatadogSDKTesting/Utils/GitInfo.swift @@ -21,17 +21,17 @@ struct GitInfo { init(gitFolder: URL) throws { workspacePath = gitFolder.deletingLastPathComponent().path - let headPath = gitFolder.appendingPathComponent("HEAD") + let headPath = gitFolder.appendingPathComponent("HEAD", isDirectory: false) let head = try String(contentsOf: headPath).trimmingCharacters(in: .whitespacesAndNewlines) if head.hasPrefix("ref:") { var mergePath = head mergePath.removeFirst(4) mergePath = mergePath.trimmingCharacters(in: .whitespacesAndNewlines) self.branch = mergePath - let refData = try String(contentsOf: gitFolder.appendingPathComponent(mergePath)) + let refData = try String(contentsOf: gitFolder.appendingPathComponent(mergePath, isDirectory: false)) commit = refData.trimmingCharacters(in: .whitespacesAndNewlines) } else { - let fetchHeadPath = gitFolder.appendingPathComponent("FETCH_HEAD") + let fetchHeadPath = gitFolder.appendingPathComponent("FETCH_HEAD", isDirectory: false) if let records = FetchHeadRecord.from(file: fetchHeadPath)?.filter({$0.commit == head}), records.count > 0 { @@ -44,7 +44,7 @@ struct GitInfo { commit = head } - let configPath = gitFolder.appendingPathComponent("config") + let configPath = gitFolder.appendingPathComponent("config", isDirectory: false) let configs = getConfigItems(configPath: configPath) if configs.count > 0 { var remote = "origin" @@ -76,9 +76,10 @@ struct GitInfo { let index = commit.index(commit.startIndex, offsetBy: 2) let folder = commit[.. CommitInfo { - let packFolder = gitFolder.appendingPathComponent("objects").appendingPathComponent("pack") + let packFolder = gitFolder + .appendingPathComponent("objects", isDirectory: true) + .appendingPathComponent("pack", isDirectory: true) var packOffset: UInt64 var indexFile: URL diff --git a/Sources/EventsExporter/CoverageExporter/CoverageExporter.swift b/Sources/EventsExporter/CoverageExporter/CoverageExporter.swift index ede3a27e..30176ba4 100644 --- a/Sources/EventsExporter/CoverageExporter/CoverageExporter.swift +++ b/Sources/EventsExporter/CoverageExporter/CoverageExporter.swift @@ -76,7 +76,8 @@ internal class CoverageExporter { if configuration.debug.saveCodeCoverageFiles { if let coverageData = coverageData, let data = try? JSONEncoder.default().encode(coverageData) { let testName = coverage.deletingPathExtension().lastPathComponent.components(separatedBy: "__").last! - let jsonURL = coverage.deletingLastPathComponent().appendingPathComponent(testName).appendingPathExtension("json") + let jsonURL = coverage.deletingLastPathComponent() + .appendingPathComponent(testName + ".json", isDirectory: false) try? data.write(to: jsonURL) } } else { diff --git a/Sources/EventsExporter/Utils/Spawn.swift b/Sources/EventsExporter/Utils/Spawn.swift index 8c579871..afff2eb5 100644 --- a/Sources/EventsExporter/Utils/Spawn.swift +++ b/Sources/EventsExporter/Utils/Spawn.swift @@ -59,11 +59,14 @@ public enum Spawn { errFile.map { try? FileManager.default.removeItem(at: $0) } } + var uuid: URL? = nil + if let output = output { outFile = nil dd_posix_spawn_file_actions_addopen(&childActions, 1, output.path, O_WRONLY | O_CREAT | O_TRUNC, 0444) } else { - outFile = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString + ".out") + uuid = Self.tempDir.appendingPathComponent(UUID().uuidString, isDirectory: false) + outFile = uuid!.appendingPathExtension(".out") dd_posix_spawn_file_actions_addopen(&childActions, 1, outFile!.path, O_WRONLY | O_CREAT | O_TRUNC, 0444) } @@ -75,7 +78,10 @@ public enum Spawn { dd_posix_spawn_file_actions_addopen(&childActions, 2, error.path, O_WRONLY | O_CREAT | O_TRUNC, 0444) } } else { - errFile = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString + ".err") + if uuid == nil { + uuid = Self.tempDir.appendingPathComponent(UUID().uuidString, isDirectory: false) + } + errFile = uuid!.appendingPathExtension(".err") dd_posix_spawn_file_actions_addopen(&childActions, 2, errFile!.path, O_WRONLY | O_CREAT | O_TRUNC, 0444) } @@ -111,12 +117,14 @@ public enum Spawn { : RunErrorCode.signal(status.status.si_status) } } + + private static let tempDir: URL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) } extension Spawn { public static func combined(_ command: String) throws -> String { - let file = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString) + let file = Self.tempDir.appendingPathComponent(UUID().uuidString, isDirectory: false) defer { try? FileManager.default.removeItem(at: file) } do { try combined(command, file: file) diff --git a/Tests/EventsExporter/Helpers/TestsDirectory.swift b/Tests/EventsExporter/Helpers/TestsDirectory.swift index 6991c321..99618336 100644 --- a/Tests/EventsExporter/Helpers/TestsDirectory.swift +++ b/Tests/EventsExporter/Helpers/TestsDirectory.swift @@ -12,7 +12,8 @@ import XCTest /// Does not create the subfolder - it must be later created with `.create()`. func obtainUniqueTemporaryDirectory() -> Directory { let subdirectoryName = "com.datadoghq.ios-sdk-tests-\(UUID().uuidString)" - let osTemporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(subdirectoryName, isDirectory: true) + let osTemporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) + .appendingPathComponent(subdirectoryName, isDirectory: true) print("💡 Obtained temporary directory URL: \(osTemporaryDirectoryURL)") return Directory(url: osTemporaryDirectoryURL) } From 65a4ea12b5ee206b8a6c097e2e2d40690de7198b Mon Sep 17 00:00:00 2001 From: Yehor Popovych Date: Tue, 17 Dec 2024 10:04:09 +0000 Subject: [PATCH 4/4] fixed Xcode version selection for code coverage --- Sources/DatadogSDKTesting/Utils/PlatformUtils.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/DatadogSDKTesting/Utils/PlatformUtils.swift b/Sources/DatadogSDKTesting/Utils/PlatformUtils.swift index 263c6e19..71277e7e 100644 --- a/Sources/DatadogSDKTesting/Utils/PlatformUtils.swift +++ b/Sources/DatadogSDKTesting/Utils/PlatformUtils.swift @@ -176,7 +176,7 @@ struct PlatformUtils { guard version.count >= 2 else { return .xcode16 } - switch version[version.startIndex...version.index(version.startIndex, offsetBy: 2)] { + switch version[version.startIndex..