Xcodeのカバレッジ計測ではなぜブランチカバレッジが取れないのだろうか?

Aaf94d61dec57d1c3cf2de7c09e472d4?s=47 Daiki Katayama
September 06, 2019

 Xcodeのカバレッジ計測ではなぜブランチカバレッジが取れないのだろうか?

Aaf94d61dec57d1c3cf2de7c09e472d4?s=128

Daiki Katayama

September 06, 2019
Tweet

Transcript

  1. 9DPEFͷΧόϨοδܭଌͰ͸ ͳͥϒϥϯνΧόϨοδ͕ औΕͳ͍ͷͩΖ͏͔ʁ J04%$ LBSJBE 1

  2. ࣗݾ঺հ • kariad / @kariad_uu • ยࢁ େथ • ΦΠγοΫεɾϥɾେ஍גࣜձࣾ

    • ςετ / ઃܭ / ໊औ͞ͳ 2 ARIAͷ੟஍
  3. ΞδΣϯμ • ίʔυΧόϨοδʹ͍ͭͯ • XcodeͰͷίʔυΧόϨοδܭଌͱදࣔ • XcodeͰͷίʔυΧόϨοδܭଌͷ࢓૊Έ • ·ͱΊ 3

  4. ίʔυΧόϨοδʹ͍ͭͯ 4

  5. ίʔυΧόϨοδͱ͸ • ιʔείʔυ͕ͲΕ͚ͩ໢ཏ͞Ε͔ͨͷׂ߹ • ԿΛ৚݅ʹ໢ཏ཰ΛݟΔ͔ෳ਺ͷ؍఺͕͋ Δ 5

  6. • C0(εςʔτϝϯτΧόϨοδ)
 ໋ྩจ໢ཏɺ໋ྩจ͕ͲΕ͚ͩ໢ཏ͞Ε ͔ͨ • C1(ϒϥϯνΧόϨοδ)
 ෼ذ໢ཏɺ෼ذ͕ͲΕ͚ͩ໢ཏ͞Ε͔ͨ • C2(σΟγδϣϯΧόϨοδ)
 ৚݅໢ཏɺ৚͕݅ͲΕ͚ͩ໢ཏ͞Ε͔ͨ

    6
  7. 9DPEFͰͷ ίʔυΧόϨοδܭଌ 7

  8. 8 ςετΛ࣮ߦͨ͠Β…

  9. ͱͯ΋؆୯ʂ 9

  10. ؆୯ʹऔΕΔ͚Ͳʜ • ͲͷछྨͷΧόϨοδΛऔΔ͔બ୒Ͱ͖ͳ͍ • ͦ΋ͦ΋ͲΜͳ৚݅ͰΧόϨοδऔͬͯΔʁ 10

  11. 9DPEFͷ ΧόϨοδܭଌͱදࣔͷ࣮ଶ 11

  12. • ·ͣ͸ԿΛ৚݅ͱͨ͠ΧόϨοδΛදࣔ ͍ͯ͠Δ͔ 12

  13. • ࢀߟʹͳΔͷ͸WWDC 2018ͷ
 What’s New in Testing
 
 https://developer.apple.com/videos/play/ wwdc2018/403/

    13
  14. • xccovͷ঺հ෦෼Ͱ໌֬ʹ
 Line coverage percentͱݴ͍ͬͯΔ • xccovͷ঺հͰ͸͋Δ͕ɺҧ͏΋ͷΛදࣔ ͍ͤͯ͞Δͱ΋ߟ͑ͮΒ͍ 14

  15. YDDPWͱ͸ • Apple͕࡞ͬͨΧόϨοδΛਓʹ΋ಡΈ΍͢ ͘දࣔͤ͞Δπʔϧ • JSONͰͷग़ྗ΋Ͱ͖Δ 15

  16. ΧόϨοδͷݩσʔλ • xccovͰ࢖͏ιʔε͸Build/Logs/Test഑Լͷ 16

  17. YDDPWΛ࢖࣮ͬͯࡍʹ ݟͯΈΔ xcrun xccov view action.xccovreport ΧόϨοδͷ֓ཁΛݟͯΈΔ 17

  18. /Users/daiki/workspace/sandbox/MVP_ConnpassEventSearch/MVP_ConnpassEventSearch/ MVP_ConnpassEventSearch/AppDelegate.swift 48.15% (13/27) MVP_ConnpassEventSearch.AppDelegate.application(_: __C.UIApplication, didFinishLaunchingWithOptions: Swift.Optional<Swift.Dictionary<__C.UIApplicationLaunchOptionsKey, Any>>) ->

    Swift.Bool 100.00% (10/10) MVP_ConnpassEventSearch.AppDelegate.applicationWillResignActive(__C.UIApplication) -> () 0.00% (0/4) MVP_ConnpassEventSearch.AppDelegate.applicationDidEnterBackground(__C.UIApplication) -> () 0.00% (0/4) MVP_ConnpassEventSearch.AppDelegate.applicationWillEnterForeground(__C.UIApplication) -> () 0.00% (0/3) MVP_ConnpassEventSearch.AppDelegate.applicationDidBecomeActive(__C.UIApplication) -> () 100.00% (3/3) MVP_ConnpassEventSearch.AppDelegate.applicationWillTerminate(__C.UIApplication) -> () 18
  19. MVP_ConnpassEventSearch.AppDelegate.applicatio nWillEnterForeground(__C.UIApplication) -> () 0.00% (0/3) ίϝϯτ΋ؚΊͯ3ߦ ΧόϨοδ͸Xcode্ͷදࣔͱҰக͢Δ 19

  20. 9DPEFͰදࣔ͞Ε͍ͯΔ ΧόϨοδ͸ߦΛج४ʹܭଌͨ͠ -JOFDPWFSBHF 20

  21. • Line coverageͱ͍͏͜ͱ͸Θ͔ͬͨ • ଞͷΧόϨοδ͸औΕͳ͍ͷ͔ʁ 21

  22. 9DPEFͷ ΧόϨοδܭଌͷ࢓૊Έ͔Β ݟ͍ͯ͘ 22

  23. ͦ΋ͦ΋ΧόϨοδ Ͳ͏΍ͬͯऔͬͯΔͷʁ • ώϯτ͸υΩϡϝϯτʹ • https://developer.apple.com/library/archive/ documentation/DeveloperTools/Conceptual/ testing_with_xcode/chapters/07- code_coverage.html 23

  24. $PEFDPWFSBHFJO9DPEFJTB UFTUJOHPQUJPOTVQQPSUFECZ --7. 24

  25. 9DPEF --7.ͷ ΧόϨοδܭଌͷ࢓૊Έ 25

  26. --7.ͷΧόϨοδܭଌ • Source-based Code Coverage • SanitizerCoverage • gcov 26

  27. --7.ͷΧόϨοδܭଌ • Source-based Code Coverage • SanitizerCoverage • gcov 27

  28. 4PVSDFCBTFE $PEF$PWFSBHF • AST΍ϓϦϓϩηοα৘ใΛ௚઀ૢ࡞͢Δ͜ ͱ͔ΒSource-basedͱݺ͹ΕΔ • ؆୯ʹ݁ՌΛࢹ֮Խ͢Δ͜ͱ·ͰՄೳ • ͓ͦΒ͘࠷΋ελϯμʔυͳ΋ͷ 28

  29. ࣮ࡍʹ4XJGUͷ 4PVSDFCBTFE$PEF$PWFSBHF ΛऔͬͯΈΔ 29

  30. 1. ΧόϨοδܭଌΛ༗ޮʹͯ͠ίϯύΠϧ 2. ੜ੒͞ΕͨϓϩάϥϜΛ࣮ߦ 3. ΧόϨοδϨϙʔτͷੜ੒ खॱ 30

  31. main.swift let number = 10 if number % 2 ==

    0 { print("ۮ਺") } ୯७ͳϓϩάϥϜͰܭଌͯ͠ΈΔ 31
  32. ΧόϨοδܭଌΛ ༗ޮʹͯ͠ίϯύΠϧ xcrun swiftc -profile-generate -profile-coverage-mapping main.swift ϓϩάϥϜ 32

  33. ੜ੒͞Εͨ ϓϩάϥϜΛ࣮ߦ ϓϩάϥϜ EFGBVMUQSPGSBX ΧόϨοδ৘ใͷੜσʔλ 33

  34. ΧόϨοδϨϙʔτΛੜ੒ xcrun llvm-profdata merge -o testprof.profdata default.profraw UFTUQSPGQSPGEBUB profrawϑΝΠϧ͸ͦͷ··ͩͱϨϙʔτΛग़ྗͰ͖Δ ܗࣜͰ͸ͳ͘ΠϯσοΫεΛ͚ͭΔඞཁ͕͋Δ

    testprof͸ࣗ෼Ͱ໋໊ 34
  35. UFTUQSPGQSPGEBUB ࠷ऴతͳΧόϨοδ৘ใ͕ೖͬͨϑΝΠϧ
 ͜ͷঢ়ଶͩͱ·ͩਓ͕ಡΊΔঢ়ଶͰ͸ͳ͍ llvm-covΛ࢖ͬͯΧόϨοδ৘ใͷදࣔ΍JSONͰ ͷग़ྗΛߦ͏͜ͱ͕Ͱ͖Δ 35

  36. xcrun llvm-cov show ./main -instr-profile=sample.profdata main.swift ߦʹରԠͨ͠ΧόϨοδ৘ใ 36

  37. ccMFUOVNCFS*OU cc ccJGOVNCFS\ ccQSJOU ۮ਺  cc^ 37

  38. • llvm-cov͸ҙ֎ͱߴػೳ • htmlͰͷग़ྗ΍ϨϙʔτܗࣜͰͷग़ྗʹ ΋ରԠ • ܭଌର৅ʹਖ਼نදݱΛ༻͍ͯಛఆͷϑΝ ΠϧΛؚΊͳ͍͜ͱ΋ • ࢖͍ํ࠷ޙʹ͚ͭ·ͨ͠

    38
  39. xcrun llvm-cov export ./main -instr- profile=testprof.profdata main.swift JSONͷΧόϨοδ৘ใ
 ௕͗ͯ͢εϥΠυʹશͯ͸ࡌΓ·ͤΜͰͨ͠ 39

  40. "totals": { "lines": { "count": 3, "covered": 3, "percent": 100

    }, "functions": { "count": 2, "covered": 2, "percent": 100 }, "instantiations": { "count": 2, "covered": 2, "percent": 100 }, "regions": { "count": 4, "covered": 4, "notcovered": 0, "percent": 100 } } llvm-covͰग़ྗͨ͠
 JSONͷҰ෦ • lines • functions • Instantiations • regions 40
  41. • ࣮͸Source-based Code Coverage͸4छྨͷ ΧόϨοδΛܭଌ͢Δ͜ͱ͕Ͱ͖Δ 41

  42. -JOFDPWFSBHF • ߦͰݟͨΧόϨοδ(ίϝϯτؚΉ) • σϑΥϧτͷઃఆ͸͜Ε • ࣮ࡍʹLine਺Ͱ෇͖߹ΘͤͯΈΔͱ׬શʹҰ க͢ΔͨΊXcodeͰද͍ࣔͯ͠Δͷ͸͜Ε 42

  43. 'VODUJPODPWFSBHF *OTUBOUJBUJPODPWFSBHF • ؔ਺ͷݺͼग़͠ճ਺ • ΠϯελϯεԽͷճ਺ 43

  44. 3FHJPODPWFSBHF • ίʔυͷྖҬͰଌΔΧόϨοδ • ࡾ߲ԋࢉࢠͳͲ΋1ߦͰ͸ͳ͘ෳ਺ͱΈͳ͢ • Source-based Code CoverageͰ͸Ұ൪ཻ౓͕ࡉ ͔͍

    44
  45. let result = 10 % 2 == 0 ? true

    : false 1ߦ͚ͩͷ؆୯ͳίʔυͷΧόϨοδΛݟͯΈΔ 45
  46. Line coverage Region coverage 46

  47. ɾಉ͡Line coverage͕͋ΔͷͰXcodeͰ͸ Source-based Code CoverageΛར༻ͯͦ͠͏ ɾXcode্දࣔ͞Ε͍ͯΔΧόϨοδ͸ Line coverage͕ͩSource-based Code Coverage

    ͱ͍͏͜ͱͳΒܭଌࣗମ͸4छྨߦΘΕ͍ͯ Δ͸ͣ 47
  48. ͱݴ͏͜ͱ͸Region coverage΋औಘͰ͖Δ ͷͰ͸… 48

  49. BuildϑΥϧμΛআ͘ͱ… 49

  50. • Coverage.profdataϑΝΠϧ͕ଘࡏ͢Δ • *.profdataܗࣜͳͷͰllvm-covΛ࢖ͬͯݟΔ ͜ͱ͕Ͱ͖Δ 50

  51. xcrun llvm-cov report ../../Products/Debug-iphonesimulator/ {ProductName}.app/{ProductName} -instr-profile=Coverage.profdata 51

  52. • Line coverageΑΓࡉ͔͍Region coverageΛ ؆୯ʹΈΔ͜ͱ͕Ͱ͖Δ • ͔͠͠ຊ౰ʹগ͚͔ͩ͠͠ࡉ͔͘ͳ͍͔ ΋͠Εͳ͍ 52

  53. • && ΍ || ͱ͍ͬͨ৚݅ʹ͍ͭͯ͸
 Line coverageͰݕग़Ͱ͖Δ 53

  54. struct Foo { func bar() { let number = 10

    if number % 2 == 0 || number == 10 { print("ۮ਺Ͱ10") } } } 54
  55. struct Foo { func bar() { let number = 10

    if number % 2 == 0 || number == 10 { print("ۮ਺Ͱ10") } } } struct Coverage { func hhoge() { let number = 10 if number % 2 == 0 || number == 10 { print("ۮ਺Ͱ10") } } } 55
  56. ϝιου͚ͩͰΈΔͱ100%͕ͩ
 implicit closureͱͯ͠දࣔ͞Ε͓ͯΓɺ
 ϑΝΠϧ୯ҐͰ͸໢ཏͰ͖͍ͯͳ͍͜ͱ͕ Θ͔Δ 56

  57. public static func || (lhs: Bool, rhs: @autoclosure () throws

    -> Bool) rethrows -> Bool { return lhs ? true : try rhs() } Swiftͷ࣮૷ͱͯ͠&&΍||ͷӈล͸closureͱͯ͠ ॲཧ͞ΕΔ 57
  58. • closure͕ผͰܭଌ͞Ε͍ͯΔ • ͦͷཧ༝Λ஌ΔͨΊʹSource-based Code Coverage͕ͲͷΑ͏ʹऔಘ͞Ε͍ͯΔ͔ Λ؆୯ʹݟͯΈΔ 58

  59. • Source-based Code Coverage͸LLVM͕ఏڙ ͢ΔCode Coverage Mapping Formatͱ͍͏ ΋ͷΛར༻ͯ͠ܭଌ͍ͯ͠Δ •

    ΦϓγϣϯΛ͚ͭͯίϯύΠϧͨ͠ࡍʹ Ϛοϐϯά༻ͷσʔλ͕ຒΊࠐ·ΕΔ 59
  60. • ͜ͷϚοϐϯάϑΥʔϚοτ͸LLVMΛ όοΫΤϯυͱͯ࣋ͭ͠ϑϩϯτΤϯυ ͷϢχόʔαϧϑΥʔϚοτΛ໨ࢦͯ͠ ͍Δ • LLVMΛόοΫΤϯυʹ΋ͭશͯͷϑϩϯ τΤϯυͰڞ௨ͯ͠औಘͰ͖ΔΧόϨο δ 60

  61. • LLVMͷυΩϡϝϯτʹCoverage Mapping Formatʹ͍ͭͯઆ໌ͨ͠΋ͷ͕ଘࡏ͢Δ
 https://llvm.org/docs/ CoverageMappingFormat.html • ͦΕʹΑΔͱϑϩϯτΤϯυ͕LLVM IRΛ ੜ੒͢ΔࡍʹຒΊࠐ·ΕΔ

    61
  62. • ͪͳΈʹSwiftcͰ͸IRGen.cppͷ
 performIRGeneration()
 
 GenDecl.cppͷ
 IRGenerator::emitGlobalTopLevel()
 
 GenCoverageͷ
 IRGenModule::emitCoverageMapping() •

    llvm/ProfileData/Coverage/CoverageMappingWriter.h 62
  63. ͱ͍͏Θ͚Ͱ--7.*3Λ ݟ͍ͯ͘ 63

  64. xcrun swiftc -emit-ir -profile-generate -profile-coverage-mapping Coverage/main.swift • -emit-irΛΦϓγϣϯͱͯ͠౉͢͜ͱͰ LLVM IRΛग़ྗ͢Δ͜ͱ͕Ͱ͖Δ

    • ΧόϨοδΛ༗ޮʹͯ͠LLVM IRΛग़ྗ 64
  65. ܭଌΦϑ ܭଌΦϯ ࠩ෼͕ͨ͘͞Μ͋Δͷ͕Θ͔Δ 65

  66. @"__profc_Coverage/Coverage.swift:$S4Hoge3FooV3baryyF" = linkonce hidden global [2 x i64] zeroinitializer, section

    "__DATA,__llvm_prf_cnts", align 8 @"__profd_Coverage/Coverage.swift:$S4Hoge3FooV3baryyF" = linkonce hidden global { i64, i64, i64*, i8*, i8*, i32, [2 x i16] } { i64 1740746221156547191, i64 0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift:$S4Hoge3FooV3baryyF", i32 0, i32 0), i8* bitcast (void ()* @"$S4Hoge3FooV3baryyF" to i8*), i8* null, i32 2, [2 x i16] zeroinitializer }, section "__DATA,__llvm_prf_data,regular,live_support", align 8 @"__profc_Coverage/Coverage.swift:__ntd_Foo_line:1:1" = linkonce hidden global [1 x i64] zeroinitializer, section "__DATA,__llvm_prf_cnts", align 8 @"__profd_Coverage/Coverage.swift:__ntd_Foo_line:1:1" = linkonce hidden global { i64, i64, i64*, i8*, i8*, i32, [2 x i16] } { i64 -7581103708217037269, i64 0, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/Coverage.swift:__ntd_Foo_line:1:1", i32 0, i32 0), i8* bitcast (void ()* @"$S4Hoge3FooVACycfC" to i8*), i8* null, i32 1, [2 x i16] zeroinitializer }, section "__DATA,__llvm_prf_data,regular,live_support", align 8 @"__profc_Coverage/Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_" = linkonce hidden global [1 x i64] zeroinitializer, section "__DATA,__llvm_prf_cnts", align 8 @"__profd_Coverage/Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_" = linkonce hidden global { i64, i64, i64*, i8*, i8*, i32, [2 x i16] } { i64 3556985279047318519, i64 0, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_", i32 0, i32 0), i8* bitcast (void ()* @"$S4Hoge3FooV3baryyF" to i8*), i8* null, i32 1, [2 x i16] zeroinitializer }, section "__DATA,__llvm_prf_data,regular,live_support", align 8 ϔομʔ෦෼ͷҰ෦ʹ஫໨ 66
  67. @"__profc_Coverage/Coverage.swift:$S4Hoge3FooV3baryyF" @"__profc_Coverage/Coverage.swift:__ntd_Foo_line:1:1" @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyFSbyKXKfu_" ͜ͷ3ͭͷάϩʔόϧม਺ʹ஫໨͢Δ ͜ͷ··ͩͱΘ͔Βͳ͍ͷͰσϚϯάϧͯ͠ΈΔ 67

  68. @"__profc_Coverage/Coverage.swift:Hoge.Foo.bar() -> () @"__profc_Coverage/Coverage.swift:__ntd_Foo_line:1:1" @"__profc_Coverage/Coverage.swift:implicit closure #1 : @autoclosure ()

    throws -> Swift.Bool in Hoge.Foo.bar() -> () @"__profc_Coverage/Coverage.swift:$S4Hoge3FooV3baryyF" @"__profc_Coverage/Coverage.swift:__ntd_Foo_line:1:1" @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyFSbyKXKfu_" 68
  69. • ͜ΕΒͷάϩʔόϧม਺͸Χ΢ϯλͱ Ϛοϐϯά৘ใΛอଘ͍ͯ͠Δ΋ͷ 69

  70. define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount =

    load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 70
  71. define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount =

    load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 71
  72. define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount =

    load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 ઌ΄ͲσϚϯάϧͨ͠ΒҎԼʹͳͬͨ΋ͷ @"__profc_Coverage/Coverage.swift:Hoge.Foo.bar() -> () 72
  73. ͲΜͳॲཧΛ͍ͯ͠Δ͔ งғؾͰ ݟ͍ͯ͘ 73

  74. define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount =

    load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 ͔͜͜ΒFoo.bar()ͷελʔτ 74
  75. define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount =

    load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 άϩʔόϧม਺(഑ྻ)͔ΒཁૉΛऔΓग़ͯ͠ɺ ϨδελpgocountೖΕ͍ͯΔ 75
  76. define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount =

    load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 pgocountͱ1ΛՃࢉͯ͠Ϩδελ0ʹೖΕ͍ͯΔ 76
  77. define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount =

    load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 define hidden swiftcc void @"$S4Hoge3FooV3baryyF"() #0 { entry: %pgocount = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyF", i64 0, i64 0) %0 = add i64 %pgocount, 1 store i64 %0, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyF", i64 0, i64 0) br label %1 ܭࢉ݁ՌͷϨδελ0Λάϩʔόϧม਺ʹอଘ 77
  78. • ͜ͷॲཧʹΑͬͯͲͷؔ਺ͷͲͷϒϩο ΫΛ௨ա͔ͨ͠ͷճ਺Χ΢ϯτ͕ߦΘΕ ͍ͯΔ 78

  79. ; <label>:3: ; preds = %2 br i1 true, label

    %4, label %7 ͨͩͷ෼ذ 79
  80. ; <label>:7: ; preds = %3 %pgocount1 = load i64,

    i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) %8 = add i64 %pgocount1, 1 store i64 %8, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) br label %9 80
  81. ; <label>:7: ; preds = %3 %pgocount1 = load i64,

    i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) %8 = add i64 %pgocount1, 1 store i64 %8, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) br label %9 ; <label>:7: ; preds = %3 %pgocount1 = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) %8 = add i64 %pgocount1, 1 store i64 %8, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) br label %9 81
  82. ; <label>:7: ; preds = %3 %pgocount1 = load i64,

    i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) %8 = add i64 %pgocount1, 1 store i64 %8, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) br label %9 ; <label>:7: ; preds = %3 %pgocount1 = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) %8 = add i64 %pgocount1, 1 store i64 %8, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) br label %9 ઌ΄ͲσϚϯάϧͨ͠ΒҎԼʹͳͬͨ΋ͷ @"__profc_Coverage/Coverage.swift:implicit closure #1 : @autoclosure () throws -> Swift.Bool in Hoge.Foo.bar() -> () 82
  83. ; <label>:7: ; preds = %3 %pgocount1 = load i64,

    i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) %8 = add i64 %pgocount1, 1 store i64 %8, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) br label %9 ; <label>:7: ; preds = %3 %pgocount1 = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/Coverage.swift: $S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) %8 = add i64 %pgocount1, 1 store i64 %8, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @"__profc_Coverage/ Coverage.swift:$S4Hoge3FooV3baryyFSbyKXKfu_", i64 0, i64 0) br label %9 ͦͷଞͷ෦෼ʹ͍ͭͯ͸ઌ΄Ͳͱಉ͡Ճࢉॲཧ 83
  84. • ͦ΋ͦ΋Χ΢ϯτ͢Δࡍʹผͷؔ਺ͱ͠ ͯΧ΢ϯτ͞Ε͍ͯͨ • ͳͷͰϑΝΠϧ୯ҐͰ͸ݟ͔ͭΔ͕ɺϝ ιου୯ҐͰ͸ݟ͔ͭΒͳ͔ͬͨ 84

  85. • ͱ͍͏͜ͱ͔ΒLine coverageͰ΋&&΍||ͷΧό ϨοδΛݟΔ͜ͱ͸Ͱ͖Δ • ࠓͷॴࢲ͕ؾ͕෇͍ͨRegion coverageͰ͔͠ݟͭ ͚ΒΕͳ͍΋ͷ͸3߲ԋࢉࢠͱ1ߦʹ·ͱΊͯॻ ͍ͨifจ͘Β͍… •

    ͦΕͰ΋Line coverageΑΓ͸ࡉ͔͘औΕΔͷͰ Region coverageΛ࢖ͬͯܭଌͯ͠΋͍͍͔΋͠Ε ͳ͍ 85
  86. ϒϥϯνΧόϨοδ Ͳ͍ͬͨ͜ • XcodeͰར༻͍ͯ͠ΔΧόϨοδܭଌ͕ Source-based Code CoverageͷͨΊɺXcode্ ͷϏϧυ͔ΒϒϥϯνΧόϨοδΛऔΔ͜ ͱ͸Ͱ͖ͳ͍ 86

  87. --7.ͷΧόϨοδܭଌ • Source-based Code Coverage • SanitizerCoverage • gcov 87

  88. --7.ͷΧόϨοδܭଌ • Source-based Code Coverage
 → Θ͔ͬͨ • SanitizerCoverage •

    gcov 
 → swiftcʹ࣮૷͞Ε͍ͯͳͦ͞͏ 88
  89. 4BOJUJ[FS$PWFSBHF • LLVMͱswiftcͰऔΕΔ΋͏ҰͭͷΧόϨοδ • Τοδͱݺ͹ΕΔΧόϨοδΛऔಘͰ͖Δ 89

  90. Τοδͱ͸ if 1 == 1 { print("hogehoge") } 90

  91. A B C ௨ৗ͸3ͭͷϒϩοΫ 91

  92. A B C D edgeΛ༗ޮʹ͢Δͱ Dͱ͍͏μϛʔϒϩοΫ͕ੜ੒͞ΕΔ 92

  93. ͔ͤͬ͘ͳͷͰ --7.*3΋ݟΑ͏ 93

  94. ; <label>:2: ; preds = %1 br i1 true, label

    %3, label %._crit_edge ._crit_edge: ; preds = %2 call void @__sanitizer_cov_trace_pc_guard(i32* inttoptr (i64 add (i64 ptrtoint ([5 x i32]* @__sancov_gen_ to i64), i64 4) to i32*)) call void asm sideeffect "", ""() br label %46 ; <label>:3: ; preds = %2 Ұ෦ൈਮ 94
  95. ; <label>:2: ; preds = %1 br i1 true, label

    %3, label %._crit_edge ._crit_edge: ; preds = %2 call void @__sanitizer_cov_trace_pc_guard(i32* inttoptr (i64 add (i64 ptrtoint ([5 x i32]* @__sancov_gen_ to i64), i64 4) to i32*)) call void asm sideeffect "", ""() br label %46 ; <label>:3: ; preds = %2 ; <label>:2: ; preds = %1 br i1 true, label %3, label %._crit_edge ._crit_edge: ; preds = %2 call void @__sanitizer_cov_trace_pc_guard(i32* inttoptr (i64 add (i64 ptrtoint ([5 x i32]* @__sancov_gen_ to i64), i64 4) to i32*)) call void asm sideeffect "", ""() br label %46 ; <label>:3: ; preds = %2 ෼ذઌʹcrit_edgeͱ͍͏΋ͷ͕ଘࡏ͢Δ 95
  96. ; <label>:2: ; preds = %1 br i1 true, label

    %3, label %._crit_edge ._crit_edge: ; preds = %2 call void @__sanitizer_cov_trace_pc_guard(i32* inttoptr (i64 add (i64 ptrtoint ([5 x i32]* @__sancov_gen_ to i64), i64 4) to i32*)) call void asm sideeffect "", ""() br label %46 ; <label>:3: ; preds = %2 ; <label>:2: ; preds = %1 br i1 true, label %3, label %._crit_edge ._crit_edge: ; preds = %2 call void @__sanitizer_cov_trace_pc_guard(i32* inttoptr (i64 add (i64 ptrtoint ([5 x i32]* @__sancov_gen_ to i64), i64 4) to i32*)) call void asm sideeffect "", ""() br label %46 ; <label>:3: ; preds = %2 ຒΊࠐ·Εͨؔ਺ΛݺΜͰ͍Δ 96
  97. • μϛʔϒϩοΫͱ͸LLVM IRͰຊདྷԿ΋ͳ ͍෼ذઌʹൃੜ͢ΔϒϩοΫ 97

  98. ΧόϨοδΛݟͯΈΔ 98

  99. • SanitizerCoverage͸ग़ྗ͕໘౗ • sancovͱ͍͏toolΛ࢖͏ඞཁ͕͋Δ • LLVMΛผ్DL͠ͳ͍ͱೖ͍ͬͯͳ͍ 99

  100. xcrun swiftc -sanitize=address -sanitize-coverage=edge Coverage/main.swift ASAN_OPTIONS=coverage=1 ./main;wc -c *.sancov main.****.sancov

    100
  101. sancov -print-coverage-stats main.52871.sancov ./main all-edges: 5 cov-edges: 5 all-functions: 4

    cov-functions: 4 ૯edge਺ͱΧόʔͨ͠edge਺͕ݟΕΔ 101
  102. sancov -symbolize main.52871.sancov ./main > main.symcov HTMLͰݟΔͨΊʹ͸γϯϘϥΠζͯ͠ ͋͛Δඞཁ͕͋Δ 102

  103. { "covered-points" : ["100000b2d", "100000c22", "100000c9f", "100000ccf", "100000d1c"], "binary-hash" :

    "5CCB31D0F47A427284B9EE58B2C89557D382CD0B", "point-symbol-info" : { "<invalid>" : { "$ss5print_9separator10terminatoryypd_S2StFfA0_" : { "100000c9f" : "0:0" }, "$ss5print_9separator10terminatoryypd_S2StFfA1_" : { "100000ccf" : "0:0" }, "asan.module_dtor" : { "100000d1c" : "0:0" }, "main" : { "100000b2d" : "0:0", "100000c22" : "0:0" } } } } 103
  104. • llvm/tools/sancov/ ഑Լʹ͋Δ
 coverage-report-server.pyͰ
 HTTPαʔόΛىಈ͢Δ • coverage-report-server.py΋brewͰΠϯε τʔϧͨ͠LLVMʹ͸ແ͔ͬͨͷͰผ్DL 104

  105. python3 coverage-report-server.py --symcov ~/workspace/Coverage/ main2.symcov --srcpath ~/workspace/Coverage/ ຊདྷ͸ϑΝΠϧ໊͕දࣔ͞ΕΔ͸͕ͣͩɺinvalidʹͳͬͯ͠· ͍ݟΕͳ͔ͬͨ(sancov͕Linux͔͠αϙʔτ͍ͯ͠ͳ͍ͷͰͦΕ ͕ݪҼ͔΋͠Εͳ͍)

    105
  106. • edgeΧόϨοδΛҰԠݟΕΔ • ͔͠͠edgeΧόϨοδ͸෼ذͷ਺Λج४ ʹݟ͍ͯΔͱ͍͏Θ͚Ͱ͸ͳ͍ͷͰϒϥ ϯνΧόϨοδͱ͸গ͠ҟͳΔ 106

  107. ϒϥϯνΧόϨοδ͸ औΕΔͷ͔ 107

  108. औΕ·ͤΜʂʂ 108

  109. • edgeͱϒϥϯν͸ҟͳΔ΋ͷͳͷͰݫີ ʹϒϥϯνΧόϨοδΛऔΔ࢓૊Έ͸ແ ͔ͬͨ • ͦ΋ͦ΋ݱঢ়ͷ΋εςʔτϝϯτΧό ϨοδͰ͸ແ͔ͬͨ(Line coverage) 109

  110. ँࡑ 110

  111. ϓϩϙʔβϧͷҰ෦ • ҰํͰίʔυΧόϨοδʹ͸͍͔ͭ͘छྨ͕ ͋ΓɺXcodeͰ͸ͦͷҰͭͰ͋Δεςʔτϝ ϯτΧόϨοδ͔͠ܭଌͰ͖ͳ͍ͱ͍͏͜ͱ Λ͝ଘ஌Ͱ͠ΐ͏͔ɻ 111

  112. ϓϩϙʔβϧͷҰ෦ • ҰํͰίʔυΧόϨοδʹ͸͍͔ͭ͘छྨ͕ ͋ΓɺXcodeͰ͸ͦͷҰͭͰ͋Δεςʔτϝ ϯτΧόϨοδ͔͠ܭଌͰ͖ͳ͍ͱ͍͏͜ͱ Λ͝ଘ஌Ͱ͠ΐ͏͔ɻ 112 XcodeͰݟΕΔͷ͸ Line coverageͰ͢ʂ

  113. • ͨͩ͠Region coverageͱ͍͏Line coverage ΑΓࡉ͔͍΋ͷ͕؆୯ʹΈΔ͜ͱ͕Ͱ͖ Δ • %ͰͳΒ͹ҰԠedge coverage΋ݟΔ͜ͱ͸ Ͱ͖Δ

    113
  114. ͦΜͳίʔυΧόϨοδΛ Ͳ͏ͯ͠ͱΓ͍͔ͨ 114

  115. • ΧόϨοδ͸Ͳ͕͜ॻ͚͍ͯͳ͍͔Λݟ Δ͜ͱ͕Ͱ͖Δπʔϧ • Ͳ͕͜ॻ͚͍ͯͳ͍͔෼͔Ε͹௥Ճ͢Δ ͜ͱ΋Ͱ͖Δ • (ͨͩ͠ॻ͚͍ͯͳ͍ͱ͜ΖΛશͯॻ͘΂ ͖͔Ͳ͏͔͸ผͷ࿩) 115

  116. • ࡉ͔͘νΣοΫͯ͘͠ΕΔ΄Ͳؾ͕෇͔ ͳ͔ͬͨͱ͜Ζʹؾ͕͚ͭΔ • ͋͘·Ͱ΋ิॿπʔϧͱͯ͠࢖͏ 116

  117. ·ͱΊ • XcodeͰ͸Line coverage͕දࣔ͞Ε͍ͯΔ • গ͠ࡉ͔͍Region coverage΋औΕΔ • ϒϥϯνΧόϨοδ͸औΕͳ͍͕ɺedgeͰͷˋͳ Β(ҰԠ)ݟΕΔ

    • ΧόϨοδΛ্खʹ׆༻ͯ͠ྑ͍ςετϥΠϑΛ 117
  118. ࢀߟࢿྉ • Code Coverage - Apple Developer
 https://developer.apple.com/library/archive/documentation/ DeveloperTools/Conceptual/testing_with_xcode/chapters/07- code_coverage.html

    • Source-based Code Coverage - Clang 10 documentation
 https://clang.llvm.org/docs/ SourceBasedCodeCoverage.html#exporting-coverage-data • LLVM Code Coverage Mapping Format - LLVM 10 documentation
 https://llvm.org/docs/CoverageMappingFormat.html 118
  119. • llvm-cov - emit coverage information - LLVM 10 documentation


    https://llvm.org/docs/CommandGuide/llvm-cov.html • SwiftίϯύΠϥͷΞʔΩςΫνϟ
 https://qiita.com/rintaro/items/3ad640e3938207218c20 • https://github.com/apple/swift • SwiftίϯύΠϥͷManglingͷษڧํ๏
 https://qiita.com/omochimetaru/items/ 60b0d50146e0abbc9b7b 119
  120. • LLVM Language Reference Manual - LLVM 10 documentation
 https://llvm.org/docs/LangRef.html

    • AddressSanitizerFlags
 https://github.com/google/sanitizers/wiki/ AddressSanitizerFlags 120
  121. ͓·͚ 121

  122. YDDPWͷ࢖͍ํ 122

  123. xcrun xccov view *.xccovreport ϑΝΠϧຖʹԿˋͱ͍ͬͨΞϓϦέʔ γϣϯશମͷΧόϨοδͷ֓ཁΛݟΔ͜ ͱ͕Ͱ͖Δ 123

  124. xcrun xccov view *.xccovarchive/ --file *.swift ಛఆͷϑΝΠϧͷԿߦ໨͕Կճ࣮ߦ͞Ε͔ͨ ͷৄࡉΛݟΔ͜ͱ͕Ͱ͖Δ ·ͨϑΝΠϧΛࢦఆ͢Δࡍʹ͸
 xcrun

    xccov view —file-list *.xccovarchive/
 Ͱ֤ϑΝΠϧͷύεΛ֬ೝͰ͖Δ 124
  125. MMWNDPWͷ࢖͍ํ 125

  126. • https://llvm.org/docs/CommandGuide/llvm- cov.html • جຊ͜͜ʹॻ͍ͯ͋Γ·͢ 126

  127. • 3छྨͷग़ྗํ๏ • show / report / export • show

    - ͲͷҐஔ͕௨ա͍ͯ͠ͳ͍͔͕ιʔε ίʔυͱରԠ͚ͮΒΕͯݟΕΔ • report - ֤ϑΝΠϧͷLine / Function / Region coverageͷsummary͕ҰཡͰݟΕΔ • export - JSON΍lcovܗࣜͰग़ྗ͕Ͱ͖Δ 127
  128. • xcrun llvm-cov -show -format html -show- regions -instr-profile=**.profdata
 ͰHTMLͰιʔείʔυͱରԠͨ͠Region

    coverageΛݟΔ͜ͱ͕Ͱ͖Δ 128
  129. 129

  130. • xcrun llvm-cov report -ignore-filename- regex=[pattern] -instr-profile=*.profdata • ਖ਼نදݱͰܭଌ͠ͳ͍ϑΝΠϧΛࢦఆͰ ͖Δ

    • -ignore-filename-regex=.*ViewController.swift
 ͰViewControllerΛআ֎͢ΔͳΜͯ͜ͱ΋ 130
  131. ͋Γ͕ͱ͏͍͟͝·ͨ͠ 131 Twitter: @kariad_uu