kariadの戯言

思ったこととか感じたこととか何か書きます

飲酒プログラミング -ほぼリーディング-

ごきげんよう、かりあどです。 今回は飲酒プログラミングAdvent Calendarを書いていきます。

adventar.org

実は1日前に書く予定だったのですが、色々環境構築でハマってしまい後ろが空いていたので1日後ろにこっそりずらしました😇 (今までは会社貸与PC上に環境作っていたのですが、退職に伴い個人PCに作り直しました)

本日のワイン

f:id:kariaduu:20191219160520p:plain:h300

井筒ワイン竜眼です。

自分はワインが好きなのですが、別に詳しいとかそんなことは全くなく適当な感想を書いているかもしれないのでその点はご容赦ください🙏 今回飲む竜眼種ですが、主に長野県で栽培されており、なかでも善光寺で栽培されていたことから善光寺ブドウとも呼ばれているらしいです。 飲んだ感想としては辛口なのですが、強いドライ感はなくすっきりした味わいの中にほのかに香る果実感と言った感じで日本食にとても合いそうな味わいです。 ざっくりいうと辛口よりのほんのり甘口と言った感じでしょうか。

本日のお題

今年のiOSDCでこんな発表をしました。

speakerdeck.com

このスライドの後半でSanitizerCoverageというものが出てくるのですが、発表当時深く追いきれなかったところがあるので、そこら辺をなんとなくみていきたいと思います。

そもそもこのSanitizerCoverage使ってる人いるのかな、という気持ちはあるのですが、まあ実装されている以上誰かが使っているのでしょう。

SanitizerCoverageのドキュメントは基本的にClangのものを参考にしていきます。

clang.llvm.org

ドキュメントを見ると実はSanitizerCoverageにはいくつかの種類があることがわかります。 その例として、 * Tracing PCs with guards * Inline 8bit-counters * PC-Table * Tracing PCs

などが挙げられます。まあほとんどExperimentalなんですけどね。 これらがswiftcには実装されているのか?みてみようと思います。

手がかりとしてまずはこのSanitizerCoverageに関するtestを見てみようと思います。

ちなみに今2杯目です。 そして書き始めるまでに環境構築で失敗してめっちゃ時間使いました。 ブランチを切り替えたりしていたのですが、次のようにハマったりしていました。

救いの手が

感謝、圧倒的感謝...!

実際にチェックアウトしたディレクトリ構成を見ていたら以前は/swift-source/llvmとなっていたものが/swift/llvm-project/llvmとなっていることに気がつきました。

そのあともビルドしたらCMakeのエラーがでてよくわからなかったりしていましたが、なんとかNinjaのビルドを通すことに成功したみたいなので、もう少し先へ進めそうです。(13インチMacBook Proなのでビルドに1~2時間かかってます)

ついでに3杯目を注ぎにいきます。 また余談なのですが、ワインオープナーはウイングタイプよりもソムリエナイフタイプのがきれいに開けやすいと思います。

こういうやつ

f:id:kariaduu:20191219165444p:plain:w200

testを読む

test/IRGenディレクトリにsanitize_coverage.swiftがあります。

// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=func %s | %FileCheck %s -check-prefix=SANCOV
// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=bb %s | %FileCheck %s -check-prefix=SANCOV
// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=edge %s | %FileCheck %s -check-prefix=SANCOV
// RUN: %target-swift-frontend -emit-ir -sanitize=fuzzer %s | %FileCheck %s -check-prefix=SANCOV
// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=edge,trace-cmp %s | %FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_TRACE_CMP
// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=edge,trace-bb %s | %FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_TRACE_BB
// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=edge,indirect-calls %s | %FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_INDIRECT_CALLS
// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=edge,8bit-counters %s | %FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_8BIT_COUNTERS
// RUN: %target-swift-frontend -emit-ir -sanitize=fuzzer %s | %FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_TRACE_CMP

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
  import Darwin
#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku)
  import Glibc
#elseif os(Windows)
  import MSVCRT
#else
#error("Unsupported platform")
#endif

// FIXME: We should have a reliable way of triggering an indirect call in the
// LLVM IR generated from this code.
func test() {
  // Use random numbers so the compiler can't constant fold
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
  let x = arc4random()
  let y = arc4random()
#else
  let x = rand()
  let y = rand()
#endif
  // Comparison is to trigger insertion of __sanitizer_cov_trace_cmp
  let z = x == y
  print("\(z)")
}

test()

// FIXME: We need a way to distinguish the different types of coverage instrumentation
// that isn't really fragile. For now just check there's at least one call to the function
// used to increment coverage count at a particular PC.
// SANCOV_TRACE_CMP: call void @__sanitizer_cov_trace_cmp

// SANCOV: call void @__sanitizer_cov

Swiftコンパイラのテストコードはlit(LLVM Integrated Tester)と呼ばれるツールを用いて行います。 使い方は公式ドキュメントだけではなく、日本語での説明もあるのでとても参考になります。

llvm.org

qiita.com

medium.com

qiita.com

Swiftコンパイラに関する丁寧な日本語ドキュメントは結構あるのでとても助かります。

これらを参考に先ほどのテストをまずは実行してみます。 litが上のQiitaだと swift-source/llvm/utils/lit/lit.pyとされていますが、上述したとおりswift-source/llvm-project/llvm/utils/lit/lit.pyになっていることだけ注意です。

llvm-project/llvm/utils/lit/lit.py -sv build/Ninja-RelWithDebInfoAssert/swift-macosx-x86_64/test-macosx-x86_64/IRGen/sanitize_coverage.swift

当然ですが成功しますね。 が、よくテストコードをみるとなんか怪しいです。FIXMEと書いてる時点でよく見なくても怪しいですが。

例えばこの行において

// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=edge,8bit-counters %s | %FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_8BIT_COUNTERS

-check-prefixSANCOV_8BIT_COUNTERSをチェックする内容として記述されていますが、ファイル内でそのような定義は見当たりません。 ファイルで定義されているのは

// SANCOV_TRACE_CMP: call void @__sanitizer_cov_trace_cmp
// SANCOV: call void @__sanitizer_cov

この二つだけです。 この二つ以外はテストとして記述はされているが、アサーションされていないと言った感じでしょうか?

試しにSANCOV_8BIT_COUNTERSについてClangのドキュメントを参考にこうなるであろうという期待結果のCHECKを追加してみます。

// SANCOV_8BIT_COUNTERS: __sanitizer_cov_8bit_counters

テストが失敗しました。 少なくともこの形では実装されていなさそうです。 ちなみにこちらもテストはないですが、Tracing PCs with guardsについては以下のように追記してみるとテストは通ります。

// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=edge %s | %FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_TRACE_PC_WITH_GUARD

// SANCOV_TRACE_PC_WITH_GUARD: call void @__sanitizer_cov_trace_pc_guard

上で貼ったスライドにもありますが、Tracing PCs with guardsについては実装されていることはわかります。

もうだいぶ酔ってきたのでそろそろ締めたい。

じゃあこのSANCOV_8BIT_COUNTERSが実装されているのか、についてコードを追っていくと、 それぞれを受け取るフラグの存在は確認できます。

https://github.com/apple/swift/blob/5446d333b844e96a73c7bb75b04d21d86d06d2f1/lib/Option/SanitizerOptions.cpp

うーん、今日はここまで... ここから先はまた今後調べてみます。

ワインはとても美味しかったです。 いろんなワイン飲んでいきたいですね。