モンスターグリル

モンスターグリルって店がある

monstergrill.jp

自分のよくいく五反田と恵比寿と草加にあるからどこにでもある店だと思っていたけど、全国8店舗しかなかった

メニュー自体が凝った作りでわかりやすかったんだ

肉、何g食いたいか、わかるUIだった

でもある日ダイニーが入ってメニューを見る楽しみが失われて、肉、何g食いたいかわかりにくくなってた

あれから行っていない

食事、頼めればいいものでもない

nix-darwin一括インストーラーの構築

nix-darwin一括installerの構築

adventar.org

概要

nix-darwinを導入する場合、通常はまずNix自体をインストールする必要があり、手順が煩雑になる。また、マシンの新設や初期化の際、既存と同じ環境を再現するために新しい環境で再ビルドを行うのは非効率である。

本記事では、Nixとnix-darwinのインストールを統合し、再ビルドを挟まずに既存環境を即座に再現可能なインストーラーの作成手法を解説する。

対象バージョン: Nix 2.29.2、nix-darwin 25.05

実装方針の確認

まず、既存のNixインストーラーの挙動を分析し、統合方針を整理する。

Nix公式インストーラーの仕組み

installerの実装はほとんどが以下に書かれている

上記の実装をしっかり睨むと大体以下のことがわかる

  • インストール対象となるderivationのクロージャ(closure)をすべてtarボールに固めている。
  • インストールスクリプト(install-multi-user.sh)は、ビルド用ユーザー/グループの作成、/nix/store へのデリベーションのコピー、nix-store --load-db によるDB復元、およびnix-daemonの登録等を行う。
nix-darwinのインストール手順

一方nix-darwinのinstall方式を見ていくと以下のことがわかる

  • /etc/nix-darwinに設定をcopy
  • darwin-rebuild switch
    • build
    • profile に追加
    • activate を実行

上記の二つをまとめると、

  • 現在の設定(=darwinSystem)を全てを含んだtarを作成
  • install script の中で諸々の処理の代わりにactivateを実行
  • darwin-rebuild switchによりprofileを作成

すればいいということがわかる

darwinSystemの構成と整合性

インストーラーに含める darwinSystem のderivationは、インストール後に /etc/nix-darwin/flake.nix から再現されるものと完全に一致させる必要がある。これらが異なると、インストール直後に再ビルドが走ってしまうためだ。

これは、「現在のnix-darwinのprofileに入っているもの=darwinConfiguration」をtarの中に含めてもそれが必ずしも再現可能な構成を取れるかというと別ということである

この整合性を保つため、/etc/nix-darwin/flake.nix からdotfiles管理用のGitリポジトリを inputs として参照する構成をとる。

/etc/nix-darwin/flake.nix

{
    inputs = {
        dotfile.url = "github:yourname/dotfiles";
      };
   outputs = { self, nix-darwin, dotfile, ... }: {
     darwinConfigurations."default" = nix-darwin.lib.darwinSystem {
       modules = [ dotfile.darwinModules.default ];
     };
  };
}

以上のようにdarwinModuleをexportする形で構成しておくと非常に都合がいい
余談だが、nixにflakeが導入されて移行 /etc/nixos/configuration.nix の代わりにdotfile repositoryの中で nixosConfigurationを直接管理することがしやすくなったが、新しいマシンをセットアップしてみるとこれが実際に嬉しいかというとそうでもなかったりする。
なので、/etc/nixos/ 以下や /etc/nix-darwinはgitで管理せずにdotfile側でmoduleをexportする形が嬉しいのではないかと思う

/etc/nix-dawin/flake.nix

darwinConfigurations."default" = dotfile.darwinConfigurations.default
 };

以上のように、dotfile 管理用 git repositoryの中にdarwinConfigurationsを書いてしまうと少し問題が起きる可能性がある。
できなくはないが、少しでも依存を間違えるとderivationが異なってしまうだろう

インストーラーの作成とパッチ適用

Nixのインストール処理のうち、nix-darwinの activate で代替可能な箇所(プロファイル作成やnix-daemon登録など)を削除・変更する。

  • activate: 同梱した darwinSystem の実行。
  • 設定コピー: /etc/nix-darwin への配置。
  • darwin-rebuild: $SELF(インストーラーのソース)を --override-input で指定して実行し、derivationの整合性を確保する。

patchファイルは以下のようになる

diff --git i/packaging/binary-tarball.nix w/packaging/binary-tarball.nix
index 2050384b0..d91b6bb8e 100644
--- i/packaging/binary-tarball.nix
+++ w/packaging/binary-tarball.nix
@@ -4,6 +4,8 @@
   buildPackages,
   cacert,
   nix,
+  darwinSystem,
+  self,
 }:
 
 let
@@ -12,6 +14,8 @@ let
     rootPaths = [
       nix
       cacert
+      darwinSystem.system
+      self
     ];
   };
 
@@ -32,6 +36,8 @@ runCommand "nix-binary-tarball-${version}" env ''
     --subst-var-by cacert ${cacert}
 
   substitute ${../scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
+    --subst-var-by darwin_system ${darwinSystem.system} \
+    --subst-var-by self ${self} \
     --subst-var-by nix ${nix} \
     --subst-var-by cacert ${cacert}
   substitute ${../scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
diff --git i/scripts/install-darwin-multi-user.sh w/scripts/install-darwin-multi-user.sh
index c74999512..5e2c5fc42 100644
--- i/scripts/install-darwin-multi-user.sh
+++ w/scripts/install-darwin-multi-user.sh
@@ -21,6 +21,9 @@ readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
 # create by default; set 0 to DIY, use a symlink, etc.
 readonly NIX_VOLUME_CREATE=${NIX_VOLUME_CREATE:-1} # now default
 
+readonly DARWIN_SYSTEM="@darwin_system@"
+readonly SELF="@self@"
+
 # caution: may update times on / if not run as normal non-root user
 read_only_root() {
     # this touch command ~should~ always produce an error
@@ -121,6 +124,17 @@ poly_configure_nix_daemon_service() {
           launchctl kickstart -k system/org.nixos.nix-daemon
 }
 
+poly_configure_nix_daemon_service() {
+    _sudo "activate" \
+        "$DARWIN_SYSTEM"/activate
+
+    _sudo "copy directory" \
+        /bin/cp -R "$SELF"/nix-darwin/ /etc/nix-darwin
+
+    _sudo "darwin-rebuild switch" \
+        env "PATH=$DARWIN_SYSTEM/sw/bin:$PATH" "$DARWIN_SYSTEM"/sw/bin/darwin-rebuild switch --flake /etc/nix-darwin#default --override-input nix-darwin-installer "$SELF"
+}
+
 poly_group_exists() {
     /usr/bin/dscl . -read "/Groups/$1" > /dev/null 2>&1
 }
diff --git i/scripts/install-multi-user.sh w/scripts/install-multi-user.sh
index f051ccc46..a55d2fc43 100644
--- i/scripts/install-multi-user.sh
+++ w/scripts/install-multi-user.sh
@@ -1016,19 +1016,8 @@ main() {
     create_build_group
     create_build_users
     create_directories
-    place_channel_configuration
     install_from_extracted_nix
 
-    configure_shell_profile
-
-    set +eu
-    # shellcheck disable=SC1091
-    . /etc/profile
-    set -eu
-
-    setup_default_profile
-    place_nix_configuration
-
     poly_configure_nix_daemon_service
 
     trap finish_success EXIT

この --override-input を活用する手法により、確実に同一のderivationを再現できる。

検証と注意点

今回作成したソースコードhttps://github.com/hiroqn/nix-darwin-installer にあるので、#installerをビルドすることでどのようなものがビルドされるか確認してもらうことができる

nixのinstallのテストは普段使っている環境を毎回吹っ飛ばすわけにはいかないので、UTM app 等の仮想環境を用いてmacOS上でのクリーンインストールテストを行うことを推奨する。

余談だが、最近macOSのセキュリティ機能のgatekeeperがかなり厳しく、ブラウザからダウンロードしたものは実行できないようになっている。インストール中に kill 9 で停止することがある。その場合は xattr -c コマンドでメタデータを削除する必要がある

謝辞:今回の検証は https://github.com/ynishinaka 氏の社内での検証を元にしている。

採用目的

herp.careers

scrapbox

scrapbox.io scrapbox.io

NixのbuildをOpenTelemetryを使ってtraceする

概要

Nixのbuildは依存関係が複雑でどのビルドにどの程度の時間がかかっているかを把握するのは難しい。

そこで OpenTelemetry を使ってbuildやcacheの取得にかかった時間を可視化することを試みる。

NixはC++で書かれているので GitHub - open-telemetry/opentelemetry-cpp: The OpenTelemetry C++ Client を使ってNix自体にpatchを当てることでtraceの機能を実現することもできるが

  • Observabilityを上げる上でコード変更を伴わない方がversionの変更に対応しやすい
  • C++を書きたくない

といった理由から、今回はNixのsource codeを変更せずに可観測性を上げることを目標とする。ちなみに後者の理由が9割である。

実装方針(build)

Nixのoptionに preBuildHookpostBuildHook というものがある。これはbuildの前と後で任意のscriptを実行させることができる。

buildの開始と終了がわかればtrace情報が作成できるので、これらを使っていく。

ちなみに、検証した結果hookは以下のような仕様になっている

  • pre-build-hook$1derivationの名前$2derivationの名前.chroot が入った状態で呼ばれる。
  • post-build-hookDRV_PATH OUT_PATHS といった環境変数が入った状態で呼ばれる。
  • pre-build-hook は sandbox optionが有効になっていないと呼ばれない

また、shell scriptからtrace情報を送信するためのツールとして otel-cli というものがあり、今回はこれで送信する。

github.com

pre-build-hookとpost-build-hookの間で情報を受け渡す手段が用意されていないので、 pre-build-hookで $TMPDIR/$drv に開始時刻を書き込んで、 post-build-hookで otel-cliからtrace情報を送る方針にする。

自分の認識の限りではあるビルドの中で同じderivationがビルドされることはないのでうまくいくはず。

雑に試す場合は以下のようにする

pre-build-hook

drv=$1
name=${drv#/nix/store/}
$path/bin/date +%s.%N > /tmp/$name"

post-build-hook

drv=$DRV_PATH
name=${drv#/nix/store/}
${pkgs.otel-cli}/bin/otel-cli span \
--start $($path/bin/cat /tmp/$name) \
--end $($path/bin/date +%s.%N) \
--config $OTEL_CLI_CONFIG \
-n $name \
-s nix-build \
--fail

dateコマンドやotel-cliへのコマンドは当然絶対PATHで用意すること

helloコマンドをビルドした感じは以下の様な感じ

実装方針(cache)

上記のtraceはbuildの時間しか計測されず、cacheを取得している時間は含まれていない

また、cacheの取得はbuildではないので hookを使うことができない

しかしcacheをhttpで取得している場合はproxyを使うことでtrace情報を取得できる可能性がある

今回はEnvoy proxyを使ってcacheのtrace情報を取得する。

envoyのtraceに関しては公式のドキュメントがあるので、以下を読む。

www.envoyproxy.io

ドキュメントによると、traceの設定をした上で traceparent headerをつければ送ってくれるようだ

ただ、cacheの取得のリクエストに対して traceparent headerにtrace idを乗せた上で送る方法を考える必要がある。

nix側のコードを見た感じではそのような設定はないので、今回は traceparent headerを載せるenvoy proxyとtraceの送信を行うproxyの2段構成でやる。

つまり 、nix -> (traceparent headerをつけるproxy:8081) -> (cache.nixos.orgにrequestを通しながらtraceの送信を行うproxy:8080) といった感じだ。

trace id を envoyのconfigに対して直接書き込でおけば比較的簡単に1段目のproxyは実装することができるが、今回はビルドごとにidを発行したい。

なので、envoyのconfigを動的に変更する xDS configuration API を使う。

www.envoyproxy.io

xDSはistioとかを使っていると比較的に簡単に使うことができるのだが流石に大袈裟な仕組みになってしまうので、今回はファイルシステムの変更を検知して設定を書き換えてくれるDynamic from filesystemを使う

Configuration: Dynamic from filesystem — envoy 1.27.0-dev-f12afb documentation

ファイルシステムの変更検知にどのsyscallを使っているのか知らないが mvコマンド相当を使わないといけないらしい。

雑にやる場合、以下の様になる。

export ENVOY_LDS_FILE_TMP="$TMPDIR/tmp.json"
jq --arg traceparent "$TRACEPARENT" \
  '.resources[0].filter_chains[0].filters[0].typed_config.route_config.request_headers_to_add[0]={"header": {"key": "traceparent","value": $traceparent}}' \
  ${./lds.json} > "$ENVOY_LDS_FILE_TMP"
rm "$ENVOY_LDS_FILE"
mv "$ENVOY_LDS_FILE_TMP" "$ENVOY_LDS_FILE"
sleep 1
nix build "$1" \
--option substituters "http://localhost:8081?priority=20" \
--no-link \
-L

sleep 1を挟んでいるのはファイルの変更検知後にenvoyの設定変更を待つためである

実行した感じは以下の様になる

tracing-nix-build

今回試しやすい様にある程度再現しやすい形でCodeをpushしておいた

github.com

ただ、現段階では動かすのにテクがいるのでそれを書いておく。

まず、上記のスクリーンショットにも使っているがjaegerを動かす必要があるが、jaeger自体がかなり複雑なので提供されているdocker imageを使うのが良いだろう。

上記のrepositoryで言うと example ディレクトリの中で docker-compose up で立ち上げてもらう。

opentelemetry collectorが 4317で立ち上がるので、example/envoy.yamlexapmle/config.json の中にある 192.168.64.1を そのアドレス(127.0.0.1など)に書き換えてもらう。

これは私の環境ではdocker自体はmacos上で動かしておりUTMを使ってnixos上でenvoyと今回のtracing-nix-buildを実行しているので、上記の様な設定になっている。

また、/tmp/envoy/lds.yaml に空ファイルを用意しておかないとenvoyが立ち上がらないので用意しておく必要がある。この値は ENVOY_LDS_FILE で変更することができる。

まとめると

nix develop
cd example
export OTEL_CLI_CONFIG_FILE=$PWD/config.json
export ENVOY_LDS_FILE=/tmp/envoy/lds.yaml
docker-compose up -d
vi envoy.yaml 
vi config.json
touch $ENVOY_LDS_FILE
envoy -c envoy.yaml &

tracing-nix-build ".#hello"

の様になる

Nixの日本語コミュがあるので、動かない人は以下で聞いてください

github.com

今後の方針

上記の実装方法ではenvoyの設定を書き換えながらやるので複数のビルドを同時に扱うことができない。

なので、netrc-filelocalhost:8081に対する認証情報をtrace idを埋め込むようにして、1段目のproxyで認証情報からtraceparent headerを組み立てると理屈上はenvoyの設定を動的に変更せずにtrace idをビルドごとに送ることができる可能性がある

また、ZeroCodeChangesではできないことも多いので、詳細なメタデータを送るために C++ でtracingの実装を追加するのも面白いかもしれない。

nixosのtoplevelをビルドした例。途中でcacheのfetchに失敗している(httpの分割requestが失敗している雰囲気)

cacheの取得を進めた上で max-jobsとcoreを8指定でした例。並行で実行されている様子がわかる

第5回しろさとTT反省会

前回は以下

hiroqn.hatenablog.com

前回からのざっくり変更点

  • zwiftのTT tune up プランを終えた
  • Rotorクランクになった
    • フロントシングルになった
    • パワーメータ導入
    • クランク長が170→165
  • ボトムブラケット新しくなった

レーニン

TT tune upを終えた

whatsonzwift.com

このプランは8週間のインターバル系?的なトレーニングで、週を追うごとに微妙に負荷が上がっており折り返しの5週目あたりからはグッとしんどくなる

特に最後の方のPower Intervalsマジでキツかった

このプラン自体は必須のメニューだけで週5日のトレーニングが必要なので、時間の捻出と肉体の回復の両方の意味で自分にはまぁまぁ大変だった

FTPが合っていれば回復が追いくぐらいの設定におそらくなっておりなんとか継続できている。これが例えば自分で適当に組んだトレーニングの場合は同程度の間隔でトレーニングするのは無理だろう

また、上のプランをやっていない期間は大体、週4〜5程度のお好みトレーニングもしくはレースをした。

お好みトレーニングは気分に左右されるので適切な負荷をかけにくいし、レースはオールアウトしがちなので継続性に難があると感じた

FTP変化

今回の大会付近のFTPは以下

RAMP TEST→238w

zwiftpower 推定値→208w

どんどんRAMP TESTと20分TTの値が乖離していく

目標設定値

今回の目標は本来パワーメータで試走した結果のW数から出す予定だった。

が、しかしパワーメータをインプレできたのは大会がある週になってしまい、一切まともに試走できていない状態で大会を迎えることになってしまう。

こんなtweetをしている場合ではない

最終的に180Wぐらいで漕いだ場合、流石に34km/hは出るだろうしポジション維持できれば35km/hぐらい出るのでは?という雑思考で目標値を34~35に決定する

本番

前回は前泊だったが今回は当日の朝出発にして、受付1時間前には着く予定だった

しかし、事故渋滞でエントリー締切の時間に会場の門をくぐることになってしまう(一敗)

駐車場でチャリの準備をしていたが、焦りのあまり計測チップを結束バンドでフォークとスポークごと結んでしまいチャリを担いで走る人間が発生してしまう(二敗)

出走前に最終のカロリーを腹に入れる予定だったのだが、そんな余裕もなく出走し途中でお腹が減る(三敗)

タイム1:32:45で32.992km/hだった

と言うわけで全然目標未達でダメだった。前回より2分程度は早くなったが自分の中でベストを尽くせなかったので、かなり悔やまれる。

ちなみに、他にもトラブルはあった

走っている途中の8周目あたりで気づいたのだがライトがハンドルについていた(四敗)

帰ってから気づいたが、前日に微妙にいじったせいでクランクの取り付けに失敗しておりチェーンがついてない状態で一切回らない状態だった(五敗)

データ

パワー

パワー

ケイデンス

ケイデンス

心拍

データから見る反省

心拍数的には最初の2周が飛ばし過ぎていた感じがする。

お腹が減ったのもあるが6〜7周目あたりで足の疲労を招いてしまい、心肺は余裕だがペダルがもう回らない状態になっていた

あと、パワーを可視化してみて改めて思ったのが出力が安定していない

もうちょっと実走を増やしてパワーを安定させる感覚を掴まなければいけないなってなった。zwiftではそれができないので

余談:パワーメータの取り付けがなぜギリギリになってしまったか

実は1月あたりから余裕を持って準備を開始したのだがトラブル続きでこのような事態になった

パワーメータ取り付けに当たって、自分の股下の長さ的に165cmのクランクの方が良さそうと言う考えになりクランク長の変更が容易なRotorにする意思決定をした

また、自分の使い方的にもフロントシングルでいいと思ったのでナローワイドのチェーンリングとRotorの INSPIDER DMを海外通販で発注した

しかし、注文したチェーンリングは通販の画像では4armsだったのに到着したものは5armsと言うトラブルが発生

仕方なく4armsのチェーンリングを注文するも、どうやら途中で紛失になった様子

そして届かぬまま2週間前になり、今回はパワメつけて走る目標を何とか達成させるためにSIGEYIというRotor互換で5arms対応のパワメを購入する

ギリギリで届いたので装着し当日へ

Rotor INSPIDER DMが余ってしまったな...

ちなみにチェーンリングはクレームを入れたところ返金されたので使わなさそうならパワメは売るか

SQLiteでLoadable Extensionsを静的リンクする

about

この記事は Run-Time Loadable Extensions で書かれている Statically Linking A Run-Time Loadable Extension をやる。

SQLiteのversionは3.40以降

Loadable Extension とは

Loadable ExtensionはSQLiteに関数とか何やかんやを足すことができる機能である。

基本的な使用方法としてはsqlite cliから .load ライブラリへのpath を使うとloadすることができる。

今回、Loadable Extension を作ることはせずに既存のextensionを使っていく。

そもそもの発端としては sqlite内でbase64のencode/decodeしたかったので以下を使う。

なんでsqlite内でbase64のencode/decodeをしたかったのか、という点は直接聞いてもらえれば。

github.com

ビルド編

実は最近はLoadable Extensionの仕様が変わっており、上記のcodeは現代的な書き方ではないので変更していく

変更し終わったコードは以下。

github.com

nix build .#sqlite3-base64 でビルドできるはず。

macの場合、 export DYLD_LIBRARY_PATH=$(nix build .#sqlite3-base64 --print-out-paths --no-link)/lib のようにしてあげれば

sqlite cli 内で.load libbasesixtyfour で loadできるようになる。

勿論、 .load 絶対PATH でも良い

以下、変更点解説など

変更点: #include <sqlite3ext.h>

拡張用のheaderになっている

変更点: sqlite3_basesixtyfour_init

ちょっと未確認 だが、.load libxxxx.so で呼び出した際に、 sqlite3_xxxx_init が呼び出されるようだが、xxxxに数字が含まれるとうまくいかなかったので名前を変えている。 libファイルの名前の制約のように感じる・

変更点: SQLITE_EXTENSION_INIT*

静的リンクする場合に備えたmacroで SQLITE_CORE が定義されているかどうかで挙動が変わるようになっている

静的リンクに向けて

実はsqlite 3.40までは既存のコードに変更を加えずに素直に静的ビルドするのが難しい

ここで良い話と悪い話がある。

SQLite: Check-in [07543d23]

良い話は上記のcommitでビルド時にフラグを追加するだけでビルドできるようになる。

悪い話は上記のcommitでbase64の機能が追加されたので、実は上のextensionをloadしなくても良くなった。

つまり sqlite3でbase64を使いたいだけの人間は そのうちリリースされる予定のsqlite 3.41を使えば良い

今回はモチベーションを失いつつも静的リンクに向けてやっていく

静的リンクビルド解説

元々どういった手法でビルドすれば良いかを明確に解説したドキュメントはないのだが、

SQLite Forum: Decimal extension in amalgamation?

SQLite Forum: Latest trunk/extensions & UDF's on tap

を見ると、とにかく shell.c に拡張用のcを結合しつつ SQLITE_EXTRA_INIT に init用の関数を指定しろ、と言うふうに読める。

ちなみに SQLiteは amalgamationという全てのソースコードを1ファイルにまとめる操作を Tcl という言語を使いつつやっているのだが

「とんでもないコードが出てますよ今」

という印象を受ける

そんな操作をしているので cat ext.c >> shell.c すれば良いという雰囲気になっている。

しかし、直近は SQLITE_EXTRA_INIT 無くなったようで、上記commitを見ると, SQLITE_SHELL_EXTFUNCSSQLITE_SHELL_EXTSRC や以下のmacroが追加されている。

#define SHELL_SUB_MACRO(base, variant) base ## _ ## variant
#define SHELL_SUBMACRO(base, variant) SHELL_SUB_MACRO(base, variant)
    /* Let custom-included extensions get their ..._init() called.
     * The WHATEVER_INIT( db, pzErrorMsg, pApi ) macro should cause
     * the extension's sqlite3_*_init( db, pzErrorMsg, pApi )
     * inititialization routine to be called.
     */
    {
      int irc = SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, INIT)(p->db);
    /* Let custom-included extensions expose their functionality.
     * The WHATEVER_EXPOSE( db, pzErrorMsg ) macro should cause
     * the SQL functions, virtual tables, collating sequences or
     * VFS's implemented by the extension to be registered.
     */
      if( irc==SQLITE_OK
          || irc==SQLITE_OK_LOAD_PERMANENTLY ){
        SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, EXPOSE)(p->db, 0);
      }
#undef SHELL_SUB_MACRO
#undef SHELL_SUBMACRO
    }
#endif

これは目を細めて凝視すると、

#define BASESF_INIT(db) sqlite3_basesixtyfour_init(db, 0, 0)
#define BASESF_EXPOSE(db, pzErr)

という initを呼ぶ macroを定義しつつ SQLITE_SHELL_EXTFUNCS=BASESFSQLITE_SHELL_EXTSRC=拡張関数.cへのpath のフラグを渡せばい良い、ということがおぼろげながら浮かんでくる。

あとはやるだけなので以下のnixファイルからビルドする。

sqlite.overrideAttrs (final: prev: {
  src = pkgs.fetchurl {
    url = "https://sqlite.org/snapshot/sqlite-snapshot-202302052029.tar.gz";
    sha256 = "sha256-MNjujQGJ/QlKs/3gUGna2G6Fmc/gAZ4qxmXflWH5DH0=";
  };
  buildInputs = prev.buildInputs ++ [ pkgs.libb64 ];
  NIX_CFLAGS_COMPILE = prev.NIX_CFLAGS_COMPILE + " -DSQLITE_CORE=1 -lb64 -isystem ${pkgs.libb64}/include/b64 -DSQLITE_SHELL_EXTSRC=${sqlite3-base64}/sqlite3_base64.c -DSQLITE_SHELL_EXTFUNCS=BASESF";
}))

実行結果

❯ sqlite3 
SQLite version 3.41.0 2023-02-05 20:29:10
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> SELECT BASE64(X'010203');
AQID
sqlite> SELECT HEX(BLOBFROMBASE64('AQID'));
010203
sqlite> 

よかったですね


SAIYO JOHO

github.com

第4回しろさとTT反省会

これは何?

チャリを買ったので3ヶ月練習して、しろさとTTというチャリを漕ぐ会に参加してきて思ったことを書く

参加は50kmの部門。

shirosato-tt.com

参加するまで

なんか世間ではzwiftという家でチャリを漕ぐやつが流行ってるらしいじゃね〜か、というインサイトを今年の夏頃に得て

チャリを...漕ぐカァ...という気持ちが高まり諸々を買い、せっかくなのでトレーニングして上のレースに出る意思決定をした。

zwiftを買ってよかったのはFTPというチャリ界の階級みたいなものに基づいて科学的な根拠のあるトレーニングができるので、根性論的なやる意味をいちいち考えさせられるような運動をさせられないのが良い

というか学生時代の部活の体力トレーニングって人間ごとにメニューを組む分けではなかったので、無意味で自己満足を育ててやっている感を出してるだけだなって思ってしまった

ちなみにFTPというのは1時間漕ぎ続けることができる限界のパワー(単位はワット)になる

練習など

練習メニューを組む上でとりあえずFTPを測らないといけないので、なんか測り方が色々あってその中でシュッと測れる RAMP testで測った

RAMP testはアスリート以外は基本的に高く出ることが多いらしい

どのみち練習メニューをこなした際にターゲットの心拍数ゾーンが設定されており、それより高く出ていればFTPの値が間違っていると考えることができるので下げたらいいと思う。

ちなみにやってみた結果は以下のようになった

8月:152w 9月:184w 11月:214w

練習メニューは目的に合わせてzwiftの初心者向けのトレーニングメニューであるFTP Builderというの一旦2ヶ月やった

途中、コロナっぽい風邪に罹って1週間休んだが概ねメニューは完遂した。

本番前

申し込みの時点では51kmあたり大体30km/hの1時間40分ぐらいを目標にしていたが

事前にzwiftや路上で漕いでみた感じでは 普通の姿勢で31km/h エアロポジションで32km/h ぐらいはいけるかなという感じだったので、 時速33km/hの1時間32分あたりを目標にした。

元々、クレアチンを取っていたが一旦飲まない期間を置いてから本番1週間前にクレアチンローディングをした。

ケトジェニックで体を慣らせば有酸素能力が一時的に上がるのでは?と思い本番2週間前から1週間ぐらいにやってみたが、科学的根拠に足る文献が無かったから今のなしで、ってなった

ただ糖質オフすると明らかに練習の負荷が高まってその後糖質をとるとめちゃ楽にこげる!ってなるから気分上の効果はあるかもしれない

本番&振り返り

チャリの会は初だったので若干不安だったがなんとかなった。

結果は1h34m30sだった

練習で50kmの距離をエアロポジション取り続けることは無かったので不安だったがずっと取り続けれたのはよかった。

心拍数のavgは166で1時間半のTTの場合はおそらくL4領域の心拍数に収まっていたらいいと思うので、そこらへんのコントロールはうまく行った説はある。

後半の足の前の筋肉がかなり"キてしまった"ので、尻で漕いでた感じがある。足がきてしまったのも一定のパワーで漕げていないことからスピードを維持しようとして不安定な出力をしてしまった感じがある。 あと、1時間半通しのトレーニングをあまりしていなかった。

zwiftで今漕いでいるパワー(ワット)が出るが、持っているチャリにはつけていないので、練習通りのパワーコントロールができていないという課題がある。

ケイデンスは安定はしてた気もするような、してないような?

気づき

とりあえずトレーニングをしてFTPを上げてくしかない

エアロポジションの研究はしたいが、ハンドルまわりを変えないといけないし計測しにくいので難しいなって思った

パワーメータは流石に欲しいかもしれない。エンジニアとして「推測するな計測せよ」というのは大事にしないといけないので、計測できる環境をまず作るか

来年の3月に第5回があるのでそれも出る。

最近はNix on raspberry pi zero をやった

イントロ

raspberry pi zeroをちょっと前に2枚買ったのが特に用途がない

強いて用があるとすれば、この前買ったserial通信経由でデータが取れるco2計測器がLinuxの方が読みやすそうだなってぐらい。 これはこのco2計測器が悪いわけではなく、macにマウントされるserial device?がちょっといまいちな挙動をしているからだ。

Amazon.co.jp: 国内生産 日本製 二波長CO2センサー搭載マイコン EPEA-CO2-NDIR-08 PC連携可 二波長NDIR方式 Dual Beam CO2モニター 二酸化炭素濃度計 : 産業・研究開発用品

というわけで 何かと開発する上で便利なので、Raspberry Pi OSにnixをいれていく。

余談だが、rasipbian OSはRaspberry Pi OSに名前が変わっていた。

install

Nixをいれるのは適当にやってもらっていいが、以下の行に注意する必要がある

nix/install.in at master · NixOS/nix · GitHub

"$(uname -s).$(uname -m)" の実行結果が Linux.armv6l_linux になることを期待しているが、 少なくとも自分の環境では Linux.armv6l になる

これは Linux.armv6l_linux が返ってくる環境が世の中にあるのかわからないのでバグなのかわからないが、armv6lのRaspberry Pi OSにNixを入れようとしている人間がzeroに近いことを演繹させてくれる有力な証拠である。

ちなみに NixOS on raspberry pi zeroをやっている人間はGitHubを見た感じでは何人かいることが観測できたので、そのうちやると思う。

build on pi

普通の環境では(ここでいう普通の環境とはflake-utilsのdefaultSystemsとか)「はい!あとはプログラムを書いて終わり!」となるが、ここはarmv6l、一切のcacheが降ってこない砂漠地帯、nix-shellが要求するbashInteractiveですらgccのビルドから始まってしまう。

というわけで gccとstdenvのビルドをやっていく。

まぁ時間さえあれば終わるでしょうと思っていてビルドを走らせていたわけだが、途中で止まってしまう。

どうやらメモリが足りなくなってOOMで落ちたっぽいエラーを観測した。足りないのは時間ではなくメモリ。

正直 パイゼロは500Mしかメモリがない、がしかし「20年前のPCでも500Mあったし流石にgccはビルドできるのは?」という気持ちも捨てがたい。そして「SD上にswapを増やしても大変な遅さになってしまうのでは?」と思いパイゼロ上でビルドするのはやめた。

なのでここからはmacでビルドしてラズパイに送る戦略をとる。

build on mac

ところでDockerDesktopという便利なものがある。何が便利かというと、マルチCPUアーキテクチャサポートがある

つまり armv6lのlinuxが豊富なメモリと屈強なCPUを積んだmacで動いてくれるわけだ。

今回は arm32v6/alpine Docker Hub にNixをインストールする戦略をとる。

余談だが NixのcrossCompile機能を使ってやるという手法もあるのだが、derivationが異なってしまう恐れを直感的に感じ取り今回はやめた。 具体的には crossCompileでビルドしたgccを使って ラズパイ上で他のものをビルドすること際に気を使う必要がある可能性を感じた、これは試していないからわからない

話を戻し、まずは bashをビルドしていく。ここで必要なのは時間と忍耐力で、具体的には一晩の時間とmacのファンの音を気にしない心だ。

ここで 一旦 ラズパイの方に できた bashを送る。 nix copy -s --to $ビルドされたbashのpath

送る際に because it lacks a valid signature と言われると思うので、nix.confに require-sigs = false を足して nix-daemonを再起動させる。

おそらく動くと思うし、bashをbuildしようとしてもbuildされずに先程送ったpathが返ってくると思う。もしビルドが始まった場合同一のderivationをビルドしようとしていない可能性が高いと思う。

あとはstdenvをビルドしていく。

あとはビルドするだけと思いきや、ここでstdenvの依存の中に含まれるpcreのtest phaseでコケてしまう。

「そんなわけがないだろう」という気持ちになるが、手元にはすでにコンパイル済みのgccがいるのでラズパイに送りさえすればビルドできるので試してみたところ pcreはビルドできてしまった。

ビルドできてしまったこと自体も「そんなわけがないだろう」という気持ちになったが、pcreがビルドされていれば後はmacのdocker側に持って来ればいいだけなので nix copy --from で持ってくる。もはやキャッチボールだ。

stdenvとbashInteractiveをラズパイ側に送りつけ、 nix-shell できることを確認する。

よかったですね

何故こういったことをやっていたのか忘れてしまったが、おおよそ、私の目的はビルドでプログラムの実行は副作用でしかないので、よかったですね。

pcre debug編

なぜpcreのテストが失敗したのか調査していくと pcregrepの -L オプションで失敗しているようだった。

pcreのソースコードを読んでいくとreaddirの挙動がおかしいように感じたので、疑似的なプログラムを書いて試してみると確かに動かない。

比較するために

  • mac
  • docker(armv6l)
  • docker(x86_64)

の三つの環境で疑似プロを動かしたところ armv6lでだけ動かない。

最初はoverlayfsあたりが悪さをしているのでは?と考えたがどうやら違うらしい。

そもそもマルチCPUアーキテクチャqemuを使っているので、そこら辺のワードと共に探っていくと以下のIssueに行き着いた。

gitlab.com

これは...ダメですね...