WSL に Flutter 環境を作ったときに dart が `Failed to memory map snapshot` で落ちたら

Flutter をセッティングしたときに、以下のような感じでエラーが出た場合

../../runtime/bin/snapshot_utils.cc: 144: error: Failed to memory map snapshot: /home/wsl-user/flutter/bin/cache/dart-sdk/bin/snapshots/kernel-service.dart.snapshot

version=2.14.0 (stable) (Mon Sep 6 13:47:59 2021 +0200) on "linux_x64"
pid=11250, thread=11253, isolate_group=(nil)((nil)), isolate=(nil)((nil))
isolate_instructions=0, vm_instructions=7f89ce0b5320
  pc 0x00007f89ce31f0dc fp 0x00007f89cb76fa50 dart::Profiler::DumpStackTrace(void*)+0x7c
  pc 0x00007f89ce0b54d4 fp 0x00007f89cb76fb30 dart::Assert::Fail(char const*, ...)+0x84
  pc 0x00007f89ce08eb86 fp 0x00007f89cb76fbd0 dart::bin::Snapshot::TryReadAppSnapshot(char const*, bool)+0x256
  pc 0x00007f89ce0927fe fp 0x00007f89cb76fc70 /mnt/i/Android/flutter/bin/cache/dart-sdk/bin/dart+0x1ccf7fe
  pc 0x00007f89ce2750a6 fp 0x00007f89cb76fd00 /mnt/i/Android/flutter/bin/cache/dart-sdk/bin/dart+0x1eb20a6
  pc 0x00007f89ce3a0288 fp 0x00007f89cb76fd80 dart::ThreadPool::WorkerLoop(dart::ThreadPool::Worker*)+0x148
  pc 0x00007f89ce3a06bc fp 0x00007f89cb76fdb0 dart::ThreadPool::Worker::Main(unsigned long)+0x5c
  pc 0x00007f89ce319e88 fp 0x00007f89cb76fe70 /mnt/i/Android/flutter/bin/cache/dart-sdk/bin/dart+0x1f56e88
-- End of DumpStackTrace
/mnt/i/Android/flutter/bin/internal/shared.sh: line 162: 11250 Aborted                 (core dumped) "$DART" --verbosity=error --disable-dart-dev $FLUTTER_TOOL_ARGS --snapshot="$SNAPSHOT_PATH" --packages="$FLUTTER_TOOLS_DIR/.packages" --no-enable-mirrors "$SCRIPT_PATH"

Flutter の Issue では再現困難のためクローズされていますが、いろいろ試したところ、WSL1の場合に発生するようでした。

使用中の WSL 環境を確認しましょう

# 一度 WSL から抜けて、Windows 側の シェルで確認
> wsl -l -v
  NAME                   STATE           VERSION
  flutter-env            Running         1

VERSION が 1 となっている場合は、以下コマンドでバージョンを変更しましょう

> wsl --set-version flutter-env 2

画像の上に矩形を描画する(HTML)

画像領域検出の可視化など、諸々の用途で「画像や写真の上に矩形を描画したいなー」ということが時々あるのでサンプルコードをメモ。

HTML上で、画像に罫線を描画するサンプル

ありがちなミスとしては、矩形の描画をImage.onload の外に書いてしまって、矩形描画のあとに画像で塗りつぶされてしまうというのをやりがちなので、実行順を意識して書きましょう。

参考 : Canvas API - Web API | MDN

AWS Lambda を開発する際のユニットテストでは、TZ=UTC にしておこう

時刻判定周りでのローカルタイムゾーンの違いは、うっかりバグの元になりやすいものですが、AWS Lambda の場合、以下のように環境変数 TZ が予約済み環境変数として変更不可能となっているようです。

docs.aws.amazon.com

  • TZ – 環境のタイムゾーン (UTC)。実行環境は、システムクロックを同期するために NTP を使用します。

つまり、AWS Lambda の実行環境ではローカルタイムゾーンUTC 固定になっているということですね。わかり易くはあるのですが、開発PC 上でや CI でユニットテストを走らせるときにローカルタイムゾーンが現地 (JSTとか) に設定されていて、テスト通ったけど本番環境でバグる、ということもありそうです。

テストを実行するときは 環境変数 TZ に UTC を設定しておくようにするのがよさそうですね。Go 言語なら Makefile に書いてしまうのもいいかもしれません。

AWS SDK for Go V2 をちょっとだけ触ってみた

AWS SDK for Go V2 が正式版となったので、ちょっとだけ触ってみました

aws.github.io

Pagination の変更

V1 から V2 に移行するにあたって、コードを書く上で最も影響がありそうな変更が、リスト系の API で使用する Pagination の変更です。

V1 では、個々のページの処理はコールバック関数で行う形でした。

   input := &s3.ListObjectsV2Input{Bucket: aws.String("test-bucket.go-aws-sdk.sample")}
    err := s3Client.ListObjectsV2Pages(input, func(output *s3.ListObjectsV2Output, lastPage bool) bool {
        for _, content := range output.Contents {
            fmt.Println(*content.Key)
        }
        return true
    })
    if err != nil {
        fmt.Printf("%v\n", err)
        panic(err)
    }

V2 ではイテレータを返す形になっていて、単純に同じコンテキストの中で for ループで回すことができます。

   input := &s3.ListObjectsV2Input{Bucket: aws.String("test-bucket.go-aws-sdk.sample")}
    paginator := s3.NewListObjectsV2Paginator(s3Client, input)

    for paginator.HasMorePages() {
        output, err := paginator.NextPage(ctx)
        if err != nil {
            fmt.Printf("%v\n", err)
            panic(err)
        }
        for _, content := range output.Contents {
            fmt.Println(*content.Key)
        }
    }

V1 だとコールバック関数内のエラーを外に引き回したりするのが面倒だったので、シンプルになるのは嬉しい変更ですが、これまでの Pagination 系関数をバッサリ消してしまったのは見切りが良すぎるというか…

モジュール化

V1 では github.com/aws/aws-sdk-go という一塊のモジュールになっていましたが、 V2 ではサービス API ごとに分割されていますね。ビルド後のバイナリサイズも相応に小さくなるようです。

上記の2つのサンプルコードを (最低限動作する形にして) それぞれ go build -ldflags="-s -w" でビルドしたところ、バイナリのサイズは 8,216,5766,545,408 と 1.7MB ほどの削減となりました。

ほか

Waiter 系は、非同期で実行される API の処理が完了するまで、状況を ポーリングしつつスレッドをブロックしてくれる機能ですね。例えば ECS のタスクが起動するまで待ってくれる TaskRunningWaiter などは、デプロイツールのようなものを作るのに便利そうです。

気になったところ

Paginator もそうですが、 session や config の呼び出し周り、あるいは DynamoDB の AttributeValue 系が別モジュールに移動しているなど、破壊的な変更が結構あるので、単純な移行は結構難しそうです。わざわざモジュール名を変えているのでしばらくは併存させるのでしょうが、いつ公開が止まるかわからんので、既存の資産をいつ、どうやって移行していくべきかが悩みどころですね…

Ubuntu18.04 に PHP7.4.8 をインストールしたときのメモ

PHP インストールのたびに前提パッケージで悩んでる気がするのでメモ。

(多分、後々バージョンが上がったらまた変わってくると思うけど)

1. anyenv 入れる

$ git clone https://github.com/anyenv/anyenv ~/.anyenv
$ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(anyenv init -)"' >> ~/.bashrc
$ exec $SHELL -l

2. phpenv 入れる

$ anyenv install phpenv
$ exec $SHELL -l

3. 前提パッケージを apt 経由で入れる

$ sudo apt update
$ sudo apt -y install build-essential
$ sudo apt -y install \
  libxml2-dev \
  libssl-dev \
  libbz2-dev \
  libcurl4-openssl-dev \
  libjpeg-dev \
  libpng-dev \
  libmcrypt-dev \
  libreadline-dev \
  libtidy-dev \
  libxslt-dev \
  autoconf \
  libkrb5-dev \
  sqlite3 \
  libsqlite3-dev \
  libonig-dev \
  libzip-dev

4. PHPを phpenv 経由で入れる

$ PHP_BUILD_EXTRA_MAKE_ARGUMENTS=-j4 phpenv install 7.4.8
$ phpenv global 7.4.8
$ exec $SHELL -l

PHP_BUILD_EXTRA_MAKE_ARGUMENTS=-j4 はビルドの並列処理数。CPUスレッド数に合わせて調整する。

AWS ECR を使う開発に便利な Vagrantfile

概要

ECS で稼働させるアプリケーションを開発していますが、Docker for Mac は何かと遅いので、Docker/ECR まわりの作業だけ Vagrant上でやるための Vagrantfile を作りました。その内容を残しておきます。

Vagrantfile

# Vagrant環境に予めインストールしておきたいものを記述する
# ここでは、AWS CLI をインストールし、 ECR へ Push するための docker login コマンドを alias として追加している
$provision_script = <<SCRIPT
apt-get update
apt-get install -y zip unzip
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install --update
rm awscliv2.zip
echo 'alias ecr-docker-login="docker login -u AWS -p $(aws ecr get-login-password) https://$(aws sts get-caller-identity --query Account --output text).dkr.ecr.${AWS_REGION}.amazonaws.com"' >> .bashrc
SCRIPT

Vagrant.configure("2") do |config|
  # お好みの Distro の Box を以下サイトから検索して指定。ここでは Ubuntu 18.04 を使用。
  # https://app.vagrantup.com/boxes/search
  config.vm.box = "bento/ubuntu-18.04"

  # メモリを増やしておく
  config.vm.provider "virtualbox" do |v|
    v.memory = 4096
    v.cpus = 2
  end

  # Windows + Hyper-V の場合
  # config.vm.provider "hyperv" do |v|
  #   v.memory = 4096
  #   v.cpus = 2
  # end

  # VM 上で初めから Docker を使えるようにしておく
  config.vm.provision "docker"
  # 初期インストールスクリプト実行
  config.vm.provision "shell", inline: $provision_script

  # Dockerコンテナでサービスするポートにホストからアクセスできるようにする
  config.vm.network "private_network", ip: "172.12.8.150"
  config.vm.network "forwarded_port", host: 8080, guest: 80

  # ホスト側のソースコード VM 内で参照するため共有しておく
  # (gitwork の下に、 github から clone したリポジトリがある想定)
  config.vm.synced_folder "#{ENV['HOME']}/gitwork", "/home/vagrant/gitwork", type: "nfs"

  # Windows の場合
  # config.vm.synced_folder "#{ENV['HOMEDRIVE']}\\#{ENV['HOMEPATH']}\\gitwork", "/home/vagrant/gitwork", type: "nfs"

  # ホスト(ローカル)での AWS Credential を共有したい場合
  # config.vm.synced_folder "#{ENV['HOME']}/.aws", "/home/vagrant/.aws", type: "nfs"
end

使用方法

vagrant up して、VMが起動したら vagrant ssh し、上記でマウントしたソースコードディレクトリに移動して docker build します。

ECR に Push するときは

$ AWS_REGION=ap-northeast-1 ecr-docker-login     # AWS_REGION には使用する ECRリポジトリがあるリージョンを指定

とした後に、ドキュメント通り、タグ付けと docker push を行います。

終わりに

今回は、Mac上に開発環境が出来上がっている状態で作成したので、ソースコードなどのリソースはホスト(Mac) のファイルシステムをマウントする形にしていますが、ゼロから作るのであれば、VagrantVMファイルシステム上に git clone して開発環境を作ったほうが何かと幸せかもしれません。

VS Code なら SSH Remote Connect を使えば開発作業もある程度シームレスにできるでしょう。

参考

qiita.com