試行錯誤のおと

日々の試行錯誤した結果です。失敗することが多い記録、それだけでっす!

Cargo.toml の編集に cargo-edit を使う

この記事は以下の記事の本記事です。 qiita.com

cargo-edit について cargo-edit の README.md をベースに簡単に日本語でまとめています。

※ 執筆時点の cargo-edit のバージョンは cargo-edit-0.1.3 です。

cargo は Rust の強力なツールの一つで、パッケージの取得、ビルドといった機能を持っています。 パッケージの追加や削除といった作業は Cargo.toml を編集して行えます。 管理するパッケージが増えるにつれ、依存解決やバージョンの変更などを行いたくなる場合、ファイルの編集というインターフェースはエディタで開くという手間もあり面倒に感じることが多くなるでしょう。 cargo-edit はそれを解決してくれるツールで、パッケージの追加・削除・確認を行うことができます。

cargo-edit: https://github.com/killercup/cargo-edit

インストール

cargo install でインストールできます。 cargo はなるべく最新版を利用することが推奨されています。

cargo install cargo-edit

使い方

cargo に以下のサブコマンドをサポートする形でインストールされます。

  • cargo add
  • cargo list
  • cargo rm

各サブコマンドは cargo のサブコマンドと同様に cargo add --help のようにサブコマンドの後に --help を指定するとヘルプが出力されます。

cargo add

cargo add は名前の通り、パッケージを追加します。

# パッケージ名@バージョン でバージョンを指定する
$ cargo add regex@0.1.41
# バージョンが指定されない場合は最新のバージョン番号を crates.io から取得する
$ cargo add rand --build
# crates.io に存在しないパッケージを追加することもできる
$ mkdir -pv ./lib/trial-and-error
$ cd ./lib/trial-and-error
$ cargo init --name local_experiment
$ cd ../../
$ cargo add local_experiment --path=lib/trial-and-error/

Cargo.toml はこうなります。

[package]
authors = ["kizkoh"]
name = "study"
version = "0.1.0"

[build-dependencies]
rand = "0.3.14"

[dependencies]
regex = "0.1.41"

[dependencies.local_experiment]
optional = false
path = "lib/trial-and-error/"

cargo list

cargo list は依存パッケージの一覧を出力します。

# cargo build しないと `Your Cargo.toml is missing.` が出力される
$ cargo build
# --tree オプションで依存木を出力できる
$ cargo list --tree
├── rand (0.3.14)
│   └── libc (0.2.14)
└── regex (0.1.73)
    ├── aho-corasick (0.5.2)
    │   └── memchr (0.1.11)
    │       └── libc (0.2.14)
    ├── memchr (0.1.11)
    │   └── libc (0.2.14)
    ├── regex-syntax (0.3.4)
    ├── thread_local (0.2.6)
    │   └── thread-id (2.0.0)
    │       ├── kernel32-sys (0.2.2)
    │       │   ├── winapi (0.2.8)
    │       │   └── winapi-build (0.1.1)
    │       └── libc (0.2.14)
    └── utf8-ranges (0.1.3)

cargo rm

cargo rmcargo add の逆の操作です。

# regex の依存を削除する
$ cargo rm regex
# rand の依存を削除する
$ cargo rm rand --build

Cargo.toml はこうなります。

[package]
authors = ["kizkoh"]
name = "study"
version = "0.1.0"

[dependencies]

[dependencies.local_experiment]
optional = false
path = "lib/trial-and-error/"

まとめ

ターミナルは常にビルドやテストのため開いていると思います。 cargo-edit を使えばエディタで Cargo.toml を開くことなくコマンド一つでパッケージの追加・削除・確認を行うことができます。

また、記事中では例に取り上げませんでしたが cargo add で追加したパッケージはアルファベット順にソートされます。 rustfmt を使ってコードはフォーマッティングできても Cargo.toml はフォーマットできません。見栄えを気にする際も便利なツールです。使わない理由はないですね!

Python の faulthandler を uwsgi で指定する

Python で実装した Web アプリケーションを uwsgi 上で動作させて運用しているときのお役立ち Tips 的なやつ。

仕事で uwsgi を運用しているのだけど、先日 uwsgi の worker がずっと busy な状態になっているプロセスが常駐していたので調査した。 strace でプロセスの状態を追うと read() で止まっていてその前後は読めない状態だった。その時は read() で待たされていることが原因だったので read() している fd を特定して、どういったことが原因で待たされているのか調査することで解決した。

けれども、もう少しスマートに解決したいという欲求はあって、 Python の faulthandler [1]なる機能を教えてもらったので調べてみた。

faulthandler を使うと指定したシグナル (SIGSEGV, SIGFPE, SIGABRT, SIGBUS, SIGILL) を受信したときにスタックトレース標準エラー出力に出力してプロセスを終了する。簡単な WSGI アプリケーションを書いて、環境変数から faulthandler を設定してみた。

#!/usr/bin/env python3.4
from werkzeug import run_simple


def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain; charset=utf-8')])
    yield b'Hello World!\n'

run_simple('127.0.0.1', 5000, app)
# exec
docker@3b813e88ee88:~$ python3 -V
Python 3.4.2
docker@3b813e88ee88:~$ PYTHONFAULTHANDLER=1 python3 ./wsgi.py &
[1] 39
docker@3b813e88ee88:~$  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
docker@3b813e88ee88:~$ curl 127.0.0.1:5000
127.0.0.1 - - [05/Jul/2016 07:28:39] "GET / HTTP/1.1" 200 -
Hello World!
# send SIGSEGV
docker@3b813e88ee88:~$ kill -s SEGV $(pidof python3)
Fatal Python error: Segmentation fault

Current thread 0x00007f7b6d866700 (most recent call first):
  File "/usr/lib/python3.4/socketserver.py", line 154docker@d733ec43cf93:~$  in _eintr_retry
  File "/usr/lib/python3.4/socketserver.py", line 236 in serve_forever
  File "/home/docker/.local/lib/python3.4/site-packages/werkzeug/serving.py", line 499 in serve_forever
  File "/home/docker/.local/lib/python3.4/site-packages/werkzeug/serving.py", line 659 in inner
  File "/home/docker/.local/lib/python3.4/site-packages/werkzeug/serving.py", line 694 in run_simple
  File "./wsgi.py", line 9 in <module>

  [1]+  Segmentation fault      PYTHONFAULTHANDLER=1 python3 ./wsgi.py

標準エラー出力スタックトレースの出力を確認できた。

あとは uwsgi を使ってアプリケーションを動かして確認するのだけど気をつけないといけないところがあって、 py-call-osafterfork [2] を有効にしないと uwsgi worker にシグナルを送信してもアプリケーションに伝達されないので注意。

# exec
# 今回 uwsgi は emperor モードで実行
# env: PYTHONFAULTHANDLER=1 を vassal の設定ファイルで指定
root@fb755eb211cb:/# uwsgi -y /etc/uwsgi/emperor.yaml
# send SIGSEGV
root@fb755eb211cb:/# ps auxf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root       127  0.0  0.0  20256  3284 ?        Ss   08:38   0:00 /bin/bash
root       148  0.0  0.0  17496  2144 ?        R+   08:39   0:00  \_ ps auxf
root        10  0.0  0.0  20256  3264 ?        Ss   08:31   0:00 /bin/bash
root       144  0.0  0.0  48236  4664 ?        S+   08:39   0:00  \_ uwsgi -y /etc/uwsgi/emperor.yaml
root       145  0.9  0.1  66480 11172 ?        S+   08:39   0:00      \_ uWSGI master
docker     146  0.0  0.1  68692 10476 ?        S+   08:39   0:00          \_ uWSGI worker 1
docker     147  0.0  0.1  68692 10476 ?        S+   08:39   0:00          \_ uWSGI worker 2
docker       1  0.0  0.0  20252  3224 ?        Ss+  08:31   0:00 /bin/bash

# worker(cheaper) に SIGSEGV を送る
docker@fb755eb211cb:~$ kill -s SEGV 146
Fatal Python error: Segmentation fault

Current thread 0x00007fa047204780 (most recent call first):
!!! uWSGI process 146 got Segmentation Fault !!!
*** backtrace of 146 ***
uWSGI worker 1(uwsgi_backtrace+0x30) [0x4635f0]
uWSGI worker 1(uwsgi_segfault+0x21) [0x4639b1]
/lib/x86_64-linux-gnu/libpthread.so.0(+0xf8d0) [0x7fa046de58d0]
/lib/x86_64-linux-gnu/libpthread.so.0(raise+0x2b) [0x7fa046de579b]
/lib/x86_64-linux-gnu/libpthread.so.0(+0xf8d0) [0x7fa046de58d0]
/lib/x86_64-linux-gnu/libc.so.6(epoll_wait+0x13) [0x7fa044f8ae33]
uWSGI worker 1(event_queue_wait+0x33) [0x456ed3]
uWSGI worker 1(wsgi_req_accept+0xd2) [0x4175f2]
uWSGI worker 1(simple_loop_run+0xb6) [0x45f7a6]
uWSGI worker 1(uwsgi_ignition+0x208) [0x463c78]
uWSGI worker 1(uwsgi_worker_run+0x25d) [0x466fbd]
uWSGI worker 1(uwsgi_run+0x3ae) [0x4674de]
uWSGI worker 1(_start+0) [0x41698e]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7fa044ec3b45]
uWSGI worker 1() [0x4169b7]
*** end of backtrace ***
DAMN ! worker 1 (pid: 146) died, killed by signal 11 :( trying respawn ...
Respawned uWSGI worker 1 (new pid: 150)
...

同様にスタックトレースの出力が確認できた。標準エラー出力に出力されるため、実際の動作では damontools の multilog や systemd のサービスとして動作させることが必要。

これで、知らないコードを追いかけずにすんで便利。2 台以上の worker でトラブル起こしていたら、1 台試してみるのいいかも。出力できる文字数が 500 文字までとか制限はあるけれども、インフラ担当者が環境変数指定すればいいだけなので、設定しておいて間違いはなさそう。

使った設定とかは下記のリポジトリに置いておく (気まぐれで消すかも)。

github.com

参照

[1]: http://docs.python.jp/3/library/faulthandler.html
[2]: https://uwsgi-docs.readthedocs.io/en/latest/Options.html#py-call-osafterfork

rust で hash 関数を扱ってみた

最近、趣味で rust を書いてる。

rust で sha256 を使って符号化したいケースがあったので調べてみた。

調べていくと rust-crypto [1] なる crate が見つかった。更新頻度も高くてよさそう。

しかし、これドキュメントがない。

ドキュメントがないので、ソースコードを追って読んでみた。 メモがてら記録としてサンプルコードを残しておくことにする。

Cargo.toml

[package]
name = "test"
version = "0.1.0"
authors = ["kizkoh"]

[dependencies]
rust-crypto = "0.2"

main.rs

extern crate crypto;

use crypto::sha2::Sha256;
use crypto::digest::Digest;

pub fn main() {
    let input: String = "test".to_string();
        
    let mut sha256 = Sha256::new();
    sha256.input_str(&input);

    println!("input: {}", input);
    println!("digest: {}", sha256.result_str());
}
$ cargo run                                                                                                                                         [master]
     Running `target/debug/test`
input: test
digest: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
$ echo -n "test" | openssl dgst -sha256
(stdin)= 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08

ちゃんと openssl と同じ結果になる。

[1] https://crates.io/crates/rust-crypto