先週書いた記事の続き
「ブログズミ: メモリ不足で gcc が kill される場合になんとかビルドする」
上記記事の対応で CircleCI 上でいくつかメモリリークなどの検知がされたのですが、誤検知を無視したりコード修正したりしてエラーが出ない状態にしていったものの、テストコードのビルドが最後まで完了しきらない状態に陥ってしまいました。
状態としては、無出力タイムアウトが発生していたので CircleCI の no_output_timeout を延ばして回避しようとしました。
しかし、5時間とかにしても一向に終わらない。。
エラー(警告)が出ない状態であれば ggc の設定を緩和しても kill されることはなくなったので、コンパイル速度優先の設定にしても無出力タイムアウトしてしまいダメ。
長いタイムアウト設定で何度もトライアンドエラーしてるとクレジット消費に悪影響が出るので、一旦 CircleCI を諦めて他のサービスを検討することにしました。
なるべくリソース豊富はところ、かつビルド時間が無料プランの制限を圧迫しないものを探しています。
各サービスのリソース状況はこちらのリポジトリで調べたものを参考にしています。
https://github.com/srz-zumix/ci-specs
拙著にも記載してます。(宣伝)
https://srz-zumix.booth.pm/
以下、随時更新していきます。
1. GitHub Actions (kill)
2. CircleCI (no output timeout)
3. RazorOps (kill)
4. AzurePipelines (timeout 360min)
2022年2月17日木曜日
gcc -fanalyzer が実行しきれる CI サービスを求めて・・・
2022年2月10日木曜日
メモリ不足で gcc が kill される場合になんとかビルドする
Process completed with exit code 137.
g++: fatal error: Killed signal terminated program cc1plus
compilation terminated.
上記のようなエラーで gcc が終了した場合、実行環境のメモリ不足が原因の場合があります。
このような場合どうしたらよいのでしょうか?
正解は「メモリを増やす」です!
このような場合どうしたらよいのでしょうか?
正解は「メモリを増やす」です!
・・・ですが。
そうはいかない場合もありますよね。特に筆者は CI サービスを無料の範囲で使っているので、実行環境のスペックには制限があり、増やすことが難しいです。
それでも、それでもビルドがしたい。
・・・ので無理やりがんばります。
そうはいかない場合もありますよね。特に筆者は CI サービスを無料の範囲で使っているので、実行環境のスペックには制限があり、増やすことが難しいです。
それでも、それでもビルドがしたい。
・・・ので無理やりがんばります。
並列数を減らす
単純な話ですが make などで並列してる場合は並列数を減らしましょう。
ガーベージコレクションの設定を調整する
--param ggc-min-expand
--param ggc-min-heapsize
上記のコンパイラーオプションを使うことで、コンパイラーの GC (ガベージコレクション)の設定ができます。
それぞれどういう設定かの
ggc-min-expand は % 単位で、ヒープ拡張できる最小の割合。
RAM が 1GB 以上あれば 100% がデフォルト、それ以外の場合は 30% を下限に設定される。
ggc-min-heapsize は GC 開始する最小のヒープサイズ。この値が小さいと GC 頻度が上がる。デフォルトは 4MB〜128MB の間で RAM サイズや RLIMIT の数値で決定されるもよう。
ggc-min-expand/ggc-min-heapsize 両方を 0 にすると常に Full GC することになる。
上記のコンパイラーオプションを使うことで、コンパイラーの GC (ガベージコレクション)の設定ができます。
それぞれどういう設定かの
ggc-min-expand は % 単位で、ヒープ拡張できる最小の割合。
RAM が 1GB 以上あれば 100% がデフォルト、それ以外の場合は 30% を下限に設定される。
ggc-min-heapsize は GC 開始する最小のヒープサイズ。この値が小さいと GC 頻度が上がる。デフォルトは 4MB〜128MB の間で RAM サイズや RLIMIT の数値で決定されるもよう。
ggc-min-expand/ggc-min-heapsize 両方を 0 にすると常に Full GC することになる。
これらの値を調整することで、少メモリな環境でもビルドできるようになる。
ただし、時間はめちゃくちゃかかる。
片方(ggc-min-expand)0 だけでも相当時間かかってタイムアウトしてたので、無料 CI 環境では厳しい。
ただし、時間はめちゃくちゃかかる。
片方(ggc-min-expand)0 だけでも相当時間かかってタイムアウトしてたので、無料 CI 環境では厳しい。
gcc -fanalyzer をやりたくて ggc の調整してたけど、時間かかりすぎて終わらない・・
https://github.com/srz-zumix/iutest/pull/564
もうちょいスペック高い CI を検討してみる。
https://github.com/srz-zumix/iutest/pull/564
もうちょいスペック高い CI を検討してみる。
2021年2月1日月曜日
古い gcc が欲しくて CentOS 5 の docker image を作った話
はじめに
なぜ古い GCC が必要になったのか?
個人開発している iutest では古の環境でもなるべく使えるように頑張りたいなーという気持ちがあります。(なんかこう制約をかいくぐってなんとかするの楽しいし)
Google Test の最新版で C++11 が要求されるようになったこともあり、今後昔の言語バージョンをサポートするライブラリは減っていくのかなーと思います。
当然、新しい言語バージョンを使っているのなら、そのバージョンをターゲットとしたライブラリのほうが効率が良いと思います。
iutest でももちろんなるべく新しい言語バージョンの機能を取り込んでいくつもりです。
ここで問題になってくるのはテストです。
今までは古の環境を想定して、iutest 側で言語機能がないと Config をセットしてテストしていました。
ライブラリ内部での undefined とかシンタックスエラーはこれでも検出できるのですが、やはり「ないと仮定」しても「実際にはある」ので、実は対応してなかったってのがいくらでも出てきちゃいます。
そんな感じで、一応サポートする気持ちはあるけどテストはできる限りという状態だったため、テストのカバー範囲を広げるために日々 OS やコンパイラのテスト環境を増やしている次第です。
なぜ CentOS 5 なのか?
はじめに、古い GCC が欲しいと思ったときに Ubuntu のパッケージからできるだけ古いものをインストールしようとしました。
しかし、そこまで古いものは取ってこれませんでした。
つぎに、ソースコードからビルド・インストールすることを試しました。
こちらはなかなかビルドが通らない、あと古い環境だと認証通らなくて(GPG key とか)必要な依存物が取ってくるのも大変でした。
(H8 クロスコンパイラはネット上の情報から作ることはできましたが)
で、CentOS の古いバージョンなら取ってこれるんじゃないとアドバイスもらったところ、本家のパッケージは EoL で取ってこれなかったのですが、アーカイブがあってそこからすんなりインストールすることができました。
と、いうわけで以下。そのときの手順をまとめます。
完成形 Dockerfile
その前に先に完成した Dockerfile を載せておきます。
以下、説明の参考にしてください。
CentOS 5 で gcc 3.4.6 と pyenv を使う
Python 3 系も使いたかったので pyenv のインストールまでしました。
GCC のインストールよりもこっちが大変だった。
では、見ていきましょう。
COPY CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo
まず、CentOS 5 は既に EoL なのでリポジトリファイルを編集して、アーカイブされたパッケージを参照するように変更します。
ネットの力を借りて、それっぽく動くようにはなりましたが、「libselinux」でエラーになってしまいました。
↑の変更に加えて「libselinux」の変更も必要みたいだったので、↓を参考に設定。
無事に yum update できるようになりました。
gcc 3.4.6 のインストール
RUN yum update -y && \
yum -y install epel-release && \
yum install -y make gcc g++ nkf wget perl gettext libffi-devel \
compat-gcc-34-3.4.6-4.1.x86_64 compat-gcc-34-c++-3.4.6-4.1.x86_64
gcc 3.4.6 のインストールはこのレイヤーで終わりです。
ポイントは↑のレイヤーで対応した、パッケージ参照先の変更だけです。
アーカイブされたパッケージ群から gcc と g++ をインストールしています。
pyenv のインストール
↑の方法では Python 3 系をインストールできなかったので pyenv 使って 3 系を使えるようにしました。
というわけで、pyenv のためにいろいろソースビルドしました。CentOS5 で pyenv 使いたい。git 必要。
— ずみっくす@あつまれCIサービス (@srz_zumix) May 28, 2020
TLS 1.0/1.1 で clone できない。git をソースビルド。zlib.h ない。zlib-devel にない。zlib ソースビルド。wget TLS 1.0/1.1 でダウンロードできない。wget ソースビルド。openssl ない。openssl ソースビルド。perl ない。yum install perl イマココ
以下の記事を参考にして、ソースビルド・インストールができました!ありがとうございます。
最後に
過去のものをビルドして使うのは大変だなと学びました。
そして、そういったものを Docker image にしておくことで少しでも苦労から開放されるなーと思いました。
Docker 素晴らしい。
今回作ったイメージは DockerHub にアップしてあります。
では。
2020年7月15日水曜日
[Travis CI] Segmentation fault したときにスタックトレースを出力する設定
テストがクラッシュして失敗したときの調査が手間だなーということで対応しました。
対応したときの PR がこちらです。
差分で見ると、なにを追加したか見やすいと思います。
[Travis CI] core dump by srz-zumix · Pull Request #288 · srz-zumix/iutest
以下、ポイントを説明していきます。


まず、コアダンプファイルが出力されるように ulimit の設定をします。
また、iutest ではテスト時に複数の実行ファイルを実行するため、コアダンプファイル名に実行ファイルの名前もつけるようにしています。
(コアダンプファイルの中身から取れる気もするけど、これで十分)

%e だと 15 文字で truncate されてしまい、実行ファイル名が正しく取れなかったので以下のような対応をしました。
gdb で coredump 開くと実行ファイルがなんだったのか出力されるので、それを取得してます。


対応したときの PR がこちらです。
差分で見ると、なにを追加したか見やすいと思います。
[Travis CI] core dump by srz-zumix · Pull Request #288 · srz-zumix/iutest
以下、ポイントを説明していきます。
導入
gdb のインストール
コアダンプファイルの出力設定
まず、コアダンプファイルが出力されるように ulimit の設定をします。
また、iutest ではテスト時に複数の実行ファイルを実行するため、コアダンプファイル名に実行ファイルの名前もつけるようにしています。
クラッシュしたらスタックトレースを出力する
実行ファイルの名前解決
linux - Core dump file name truncated - Stack Overflow%e だと 15 文字で truncate されてしまい、実行ファイル名が正しく取れなかったので以下のような対応をしました。
gdb で coredump 開くと実行ファイルがなんだったのか出力されるので、それを取得してます。
for f in `find . -maxdepth 1 -name "core_x_*"`; do
COREFILE=${f}
EXECFILE=${f##*/core_x_}
if [ ! -f "${EXECFILE}" ]; then
EXECFILE=`gdb -c "${COREFILE}" -batch | grep -o -e "\./[A-Za-z_]*"`
fi
gdb -c "${COREFILE}" ${EXECFILE} -ex "thread apply all bt" -ex "set pagination 0" -batch
done
出力例

最後に
最近はローカル環境で実行確認することも減ってきたので CI 上でデバッグできるようにしておくのは便利でいいですね。
今回は以上です。
では。
2019年6月3日月曜日
lcov で "geninfo: ERROR: ***.gcno: reached unexpected end of file" が出たときにやったこと
Coveralls へのカバレッジレポートアップロードを Travis CI でやっていて、
今までは gcc + gcov でやっていたのだが、Codecov の方が clang から gcc に変更になったので、Coveralls は clang に。
と、思ったらぶつかったのでメモ。
https://github.com/srz-zumix/iutest/issues/234
まず、clang が生成する gcno のフォーマットが gcc 4.2 で、Travis CI の xenial 環境に入ってる gcc が 5.5 だったため clang + gcov でもエラーが出て集計できなかった、というのが根本的な原因と思われます。
この状態で lcov を使うと、表題のエラーになっていました。
↑の記事にあるように、llvm-gcov.sh ファイルを作成する。中身は以下の通り
あとは、lcov のオプションに --gcov-tool path/to/dir/llvm-gcov.sh とするだけです。
iutest で実際にやってますので、そちらも参考にしてみてください。
ではでは。
今までは gcc + gcov でやっていたのだが、Codecov の方が clang から gcc に変更になったので、Coveralls は clang に。
と、思ったらぶつかったのでメモ。
https://github.com/srz-zumix/iutest/issues/234
まず、clang が生成する gcno のフォーマットが gcc 4.2 で、Travis CI の xenial 環境に入ってる gcc が 5.5 だったため clang + gcov でもエラーが出て集計できなかった、というのが根本的な原因と思われます。
gcov -r *.gcda; also_run_disabled_tests.gcno:version '402*', prefer '505*' Segmentation fault (core dumped)
この状態で lcov を使うと、表題のエラーになっていました。
geninfo: ERROR: /home/travis/build/srz-zumix/iutest/test/assertion_return_tests.gcno: reached unexpected end of filelcov は gcov をバックエンドとして使っているので、おそらく内部的に gcov のエラーが置きているのではないかと予想。(ホントのところは知らんけど)
解決方法
Check Code Coverage with Clang and LCOV | Logan's Note↑の記事にあるように、llvm-gcov.sh ファイルを作成する。中身は以下の通り
あとは、lcov のオプションに --gcov-tool path/to/dir/llvm-gcov.sh とするだけです。
iutest で実際にやってますので、そちらも参考にしてみてください。
ではでは。
2019年5月13日月曜日
[C++] g++ 9.1.0 で variant が valueless_by_exception にならないケースに遭遇した
iutest の開発をしていたら、variant が valueless になったときのテストが失敗するようになった。

テストコードはこれ。
valueless_by_exception と出力されるのを期待しているが、実際には 0.2 という空になる前の値を出力している。
この valueless にするコードは、
https://ja.cppreference.com/w/cpp/utility/variant/valueless_by_exception からとってきたもので、今までは valueless になっていたのを確認している。
関連するコードをいじってはいないので、環境周りの違いだと思い確認したところ、g++ 9.1.0 では valueless にならないことを確認しました。
また、valueless な状態を作る方法を探していたら、
https://cpprefjp.github.io/reference/variant/variant/valueless_by_exception.html に AlwaysThrow でのサンプルがあったので、そちらを使って確認したところ g++ 9.1.0 でも valueless になりました。
よって、テストコードとりあえず以下のように修正しました。
一旦ここまでで記事公開させていただきます。
続報あれば、追記していきますmm
テストコードはこれ。
valueless_by_exception と出力されるのを期待しているが、実際には 0.2 という空になる前の値を出力している。
{
PrintToLogChecker ck("valueless_by_exception");
::std::variant<int, ::std::string, float> v = 0.2f;
try
{
struct S { operator int() { throw 42; } };
v.emplace<0>(S());
}
catch(...)
{
}
IUTEST_SUCCEED() << ::iutest::PrintToString(v);
}
この valueless にするコードは、
https://ja.cppreference.com/w/cpp/utility/variant/valueless_by_exception からとってきたもので、今までは valueless になっていたのを確認している。
関連するコードをいじってはいないので、環境周りの違いだと思い確認したところ、g++ 9.1.0 では valueless にならないことを確認しました。
また、valueless な状態を作る方法を探していたら、
https://cpprefjp.github.io/reference/variant/variant/valueless_by_exception.html に AlwaysThrow でのサンプルがあったので、そちらを使って確認したところ g++ 9.1.0 でも valueless になりました。
(typo がひでぇ)gcc 9.1.0 [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ https://t.co/m7V6DImr9E— ずみっくす :D (@srz_zumix) 2019年5月11日
g++ 9.1.0 は前者のパターンは valueless ならず。
— ずみっくす :D (@srz_zumix) 2019年5月11日
以前の値が残ってるみたいだけど、この挙動はなんかハマりそうな気がしないでもない。
よって、テストコードとりあえず以下のように修正しました。
{
PrintToLogChecker ck("valueless_by_exception");
::std::variant<int, float, AlwaysThrow> v = 0.2f;
try
{
struct S { operator int() { throw 42; } };
v.emplace<0>(S());
}
catch(...)
{
IUTEST_INFORM_TRUE(v.valueless_by_exception());
}
if( !v.valueless_by_exception() )
{
try
{
v = AlwaysThrow();
}
catch(...)
{
IUTEST_INFORM_TRUE(v.valueless_by_exception());
}
}
IUTEST_SUCCEED() << ::iutest::PrintToString(v);
}
これはバグ?
cpprefjp も cppreference.com も、サンプルのコメントを読むと、確実に valueless な状態にはなるとは限らないように読み取れるのだが、(規格的に)これはそういうもんなのだろうか?それとも g++ 9.1.0 のバグなのだろうか?一旦ここまでで記事公開させていただきます。
続報あれば、追記していきますmm
2015年10月27日火曜日
[Coverity] CEDEC 2015 で出題されたバグクイズ
バグクイズです!(CEDEC 2015 の問題) « Coverity Blog
CEDEC 2015 で出題されたバグクイズ。
私も実際に CEDEC に行って見てきたわけですが、
3日目の問題が直ぐに解けず、なんだか悔しかったのでこの記事を書いてる次第です。
(あーだこーだしているうちに2ヶ月も経ってしまいましたが…)
ちなみに、答えはこちらです。
CEDEC 2015 Tokyo でのバグクイズの正解を発表! « Coverity Blog
検証の際に実際に使用したソースコードはこちら。
https://github.com/srz-zumix/coverity_test/tree/master/cedec_coverity
使用したのは現時点での最新バージョンである 1.7.0 です。

残念ながら、どちらの問題も検出されませんでした。
Visual Studio にはコード分析機能が付いています。プロジェクトプロパティのコード分析で有効にできます。
これもかなり優秀なんですが…

残念ながら、どちらの問題も検出されませんでした。
Visual Studio はコード分析のときにビルドしましたが、検出できなかったので既に脱落しております。
それぞれの結果です。
どちらも delete (void*)p の問題は検出されました。
scan-build は clang の静的解析ツールです。
cygwin パッケージに入ってたので使ってみました。

結果としては clang コンパイラーと一緒で delete (void*)p の検出だけでした。
CEDEC 2015 で出題されたバグクイズ。
私も実際に CEDEC に行って見てきたわけですが、
3日目の問題が直ぐに解けず、なんだか悔しかったのでこの記事を書いてる次第です。
(あーだこーだしているうちに2ヶ月も経ってしまいましたが…)
ちなみに、答えはこちらです。
CEDEC 2015 Tokyo でのバグクイズの正解を発表! « Coverity Blog
Coverity 以外では検出できないのか?
検証の際に実際に使用したソースコードはこちら。
https://github.com/srz-zumix/coverity_test/tree/master/cedec_coverity
Cppcheck v1.70
まずは、フリーで使える C++ 静的解析の定番となっている Cppcheck です。使用したのは現時点での最新バージョンである 1.7.0 です。

残念ながら、どちらの問題も検出されませんでした。
Visual Studio コード分析
次に Visual Studio です。Visual Studio にはコード分析機能が付いています。プロジェクトプロパティのコード分析で有効にできます。
これもかなり優秀なんですが…

残念ながら、どちらの問題も検出されませんでした。
gcc/clang
最後はコンパイラーです。Visual Studio はコード分析のときにビルドしましたが、検出できなかったので既に脱落しております。
それぞれの結果です。
- gcc 5.2 [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ
- clang 3.7 [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ
どちらも delete (void*)p の問題は検出されました。
scan-build
おまけです。scan-build は clang の静的解析ツールです。
cygwin パッケージに入ってたので使ってみました。

結果としては clang コンパイラーと一緒で delete (void*)p の検出だけでした。
まとめ
Coverity すごい!!
登録:
投稿 (Atom)