Skip to content
Update debugging authored by umaumax's avatar umaumax
[[_TOC_]] [[_TOC_]]
## ワークアラウンド ## ワークアラウンド
### 🌟🌟🌟 root権限無しで、`/etc/hosts`ファイルの上書き相当の処理を実行する
[dns - associate IP with hostname without editing /etc/hosts - Unix & Linux Stack Exchange]( https://unix.stackexchange.com/questions/714953/associate-ip-with-hostname-without-editing-etc-hosts ) ### :star2::star2::star2: root権限無しで、`/etc/hosts`ファイルの上書き相当の処理を実行する
``` bash [dns - associate IP with hostname without editing /etc/hosts - Unix & Linux Stack Exchange](https://unix.stackexchange.com/questions/714953/associate-ip-with-hostname-without-editing-etc-hosts)
TMP_HOSTS_FILEPATH=./tmp_etc_hosts # 相対パスでもOK
unshare --mount --map-root-user bash -c "mount --bind --make-private "$TMP_HOSTS_FILEPATH" /etc/hosts; ping hoge.example.com" ```bash
``` TMP_HOSTS_FILEPATH=./tmp_etc_hosts # 相対パスでもOK
unshare --mount --map-root-user bash -c "mount --bind --make-private "$TMP_HOSTS_FILEPATH" /etc/hosts; ping hoge.example.com"
## デバッグについて ```
### 種類
* ライブデバッグ: プロセスにアタッチしてリアルタイムに行う ## デバッグについて
* ポストモーテムデバッグ: e.g. coredumpを利用してポストモーテム(死後)に行う
### 種類
### バグに対処する対策のステップについて考える
* 1. バグをそもそも作らない * ライブデバッグ: プロセスにアタッチしてリアルタイムに行う
* 2. 埋め込んでしまったバグを指摘される前に気がつく * ポストモーテムデバッグ: e.g. coredumpを利用してポストモーテム(死後)に行う
* 3. バグが発生してしまった後
* 3-1. バグの解析をしやすくするか ### バグに対処する対策のステップについて考える
* 3-2. 再現のしやすさ
* バグをそもそも作らない
## 値のdump * 埋め込んでしまったバグを指摘される前に気がつく
### 数値をdumpする際の注意点 * バグが発生してしまった後
言語のAPIの標準設定よりも、桁数を増やして、より正確な値として出力すること(ただし、より正確に完全一致で比較したい場合にはバイナリ値を見るべき) * 3-1. バグの解析をしやすくするか
* 3-2. 再現のしやすさ
### float, doubleのバイナリの値比較例
cpp ## 値のdump
``` cpp
#include <iomanip> ### 数値をdumpする際の注意点
#include <iostream>
言語のAPIの標準設定よりも、桁数を増やして、より正確な値として出力すること(ただし、より正確に完全一致で比較したい場合にはバイナリ値を見るべき)
int main(int argc, const char* argv[]) {
float f = 1.234f; ### float, doubleのバイナリの値比較例
std::cout << std::hex << std::setw(8) << std::setfill('0') << *reinterpret_cast<uint32_t*>(&f) << std::dec << std::endl;
cpp
double d = 1.234;
std::cout << std::hex << std::setw(16) << std::setfill('0') << *reinterpret_cast<uint64_t*>(&d) << std::dec << std::endl; ```cpp
return 0; #include <iomanip>
} #include <iostream>
```
int main(int argc, const char* argv[]) {
see: [umaumax/dumpling]( https://github.com/umaumax/dumpling ) float f = 1.234f;
rust std::cout << std::hex << std::setw(8) << std::setfill('0') << *reinterpret_cast<uint32_t*>(&f) << std::dec << std::endl;
``` rust
fn main() { double d = 1.234;
let x = 1.234_f64; std::cout << std::hex << std::setw(16) << std::setfill('0') << *reinterpret_cast<uint64_t*>(&d) << std::dec << std::endl;
println!( return 0;
"{}", }
x.to_be_bytes() ```
.iter()
.map(|n| format!("{:02x}", n)) see: [umaumax/dumpling](https://github.com/umaumax/dumpling) rust
.collect::<String>()
); ```rust
} fn main() {
``` let x = 1.234_f64;
println!(
### c++: char array to hex std::string "{}",
``` cpp x.to_be_bytes()
#include <sstream> .iter()
.map(|n| format!("{:02x}", n))
std::stringstream ss; .collect::<String>()
ss << std::hex; );
int index=0; }
for (const auto &item : test) { ```
ss << (0xff & static_cast<unsigned int>(item));
if (index % 8 == 7) { ### c++: char array to hex std::string
ss << " ";
} ```cpp
index++; #include <sstream>
}
std::cout << ss.str() << std::endl; std::stringstream ss;
``` ss << std::hex;
int index=0;
`ss`は一時変数なので、`ss << std::dec`を省略している for (const auto &item : test) {
ss << (0xff & static_cast<unsigned int>(item));
8バイトごとにスペース区切りあり if (index % 8 == 7) {
ss << " ";
### Rust: `[u8]` to hex String }
see: [umaumax/dumpling]( https://github.com/umaumax/dumpling ) index++;
}
``` rust std::cout << ss.str() << std::endl;
str_or_u8_slice ```
.iter()
.enumerate() `ss`は一時変数なので、`ss << std::dec`を省略している
.map(|(i, n)| format!("{:02x}{}", n, if i % 8 == 7 { " " } else { "" }))
.collect::<String>() 8バイトごとにスペース区切りあり
```
8バイトごとにスペース区切りあり ### Rust: `[u8]` to hex String
### 値をdumpするにはデータ量が多い場合のハッシュ計算例 see: [umaumax/dumpling](https://github.com/umaumax/dumpling)
* c++
* [cpp-examples/md5.cpp at master · umaumax/cpp-examples]( https://github.com/umaumax/cpp-examples/blob/master/hash/md5.cpp ) ```rust
* [cpp-examples/sha1.cpp at master · umaumax/cpp-examples]( https://github.com/umaumax/cpp-examples/blob/master/hash/sha1.cpp ) str_or_u8_slice
.iter()
required: .enumerate()
``` bash .map(|(i, n)| format!("{:02x}{}", n, if i % 8 == 7 { " " } else { "" }))
sudo apt install -y libboost-dev .collect::<String>()
``` ```
see: [umaumax/dumpling]( https://github.com/umaumax/dumpling ) 8バイトごとにスペース区切りあり
rust sha1
``` bash ### 値をdumpするにはデータ量が多い場合のハッシュ計算例
cargo add sha1_smol
``` * c++
* [cpp-examples/md5.cpp at master · umaumax/cpp-examples](https://github.com/umaumax/cpp-examples/blob/master/hash/md5.cpp)
``` rust * [cpp-examples/sha1.cpp at master · umaumax/cpp-examples](https://github.com/umaumax/cpp-examples/blob/master/hash/sha1.cpp)
sha1_smol::Sha1::from(&str_or_u8_slice).digest()
``` required:
#### 🔥char arrayをstd::stringに変換する際の注意点 ```bash
`std::string((const char*)raw_image[i], width*height)`のようにサイズを指定すること(バイナリデータには`'\0'`が含まれている場合があるため) sudo apt install -y libboost-dev
```
## システム全体の挙動を解析したい
### ある一定期間の間に実行されたすべてのコマンドを取得したい see: [umaumax/dumpling](https://github.com/umaumax/dumpling) rust sha1
``` bash
sudo execsnoop -d 1 ```bash
``` cargo add sha1_smol
```
### ある一定期間の間に開かれたファイルを取得したい
``` bash ```rust
sudo opensnoop -d 1 sha1_smol::Sha1::from(&str_or_u8_slice).digest()
``` ```
## あるコマンドの挙動を解析したい #### :fire:char arrayをstd::stringに変換する際の注意点
### 利用する環境変数を知りたい
``` bash `std::string((const char*)raw_image[i], width*height)`のようにサイズを指定すること(バイナリデータには`'\0'`が含まれている場合があるため)
ltrace -e getenv ld -lhoge
``` ## 他組織の人と統合動作を検証する場合
### アクセスするファイルが知りたい * 💡**何かしらの変更が加えられていないかどうか**
``` bash * 必ず確認すること
strace -e trace=file ld -lhoge * 特にバージョンや設定の変更が原因となることが多いので注意すること
```
### あるプロセスが現在開いているファイルを知りたい * どこまで問題ないかどうかを実際に手を動かして細かく切り分けすること(ある程度当たりはつけても良いが、枝刈りをしすぎると、見落としに全然気が付かなくなることに注意)
``` bash * 具体的なエラーの内容に囚われすぎずに、バージョンの不一致や環境差分、設定など広い範囲についても考えるとよい
sudo lsof -p $PID
``` ## システム全体の挙動を解析したい
* 利用している動的ライブラリの情報も取得できる ### ある一定期間の間に実行されたすべてのコマンドを取得したい
* 特に、ログファイルが一発でわかる
```bash
### 特定のプロセスの動きを一時的に停止したい sudo execsnoop -d 1
``` bash ```
kill -SIGSTOP $(pgrep hoge)
# do something ### ある一定期間の間に開かれたファイルを取得したい
kill -SIGCONT $(pgrep hoge)
``` ```bash
sudo opensnoop -d 1
### あるプロセスが現在実行されているCPUのidを知りたい ```
`trace-cmd`を利用する
## あるコマンドの挙動を解析したい
[trace cmd · Wiki · umaumax / memo · GitLab]( https://gitlab.com/umaumax/memo/-/wikis/trace-cmd#cpu%E3%81%AE%E5%88%87%E3%82%8A%E6%9B%BF%E3%82%8F%E3%82%8A%E3%81%AE%E7%9B%A3%E8%A6%96 )
### 利用する環境変数を知りたい
### コンテキストスイッチの切り替え回数をn秒ごとに監視する
下記の`cswch/s nvcswch/s`の項目を見る ```bash
``` bash ltrace -e getenv ld -lhoge
pidstat -w 1 -p $PID ```
```
### アクセスするファイルが知りたい
### 特定のパスのコマンドを置き換える方法
* 方法1: `$PATH`を置き換える ```bash
* オリジナルのシェル関数(`set_command_logger COMMAND_NAME`)がある strace -e trace=file ld -lhoge
* 方法2: `mount --bind`を利用する ```
* 直接ファイルパス指定で起動するケースでも置換可能
* ただし、元のファイルが呼べなくなる ### あるプロセスが現在開いているファイルを知りたい
### g++コマンドの引数のトレース ```bash
`cxx.sh` sudo lsof -p $PID
``` bash ```
#!/usr/bin/env bash
set -x * 利用している動的ライブラリの情報も取得できる
g++ "$@" -g3 -O0 * 特に、ログファイルが一発でわかる
```
### 特定のプロセスの動きを一時的に停止したい
オプションを強制的に追加することもできる
```bash
## ネットワーク kill -SIGSTOP $(pgrep hoge)
### TCP通信の中身を気軽に確認したい # do something
詳しく確認するにはtcpdumpコマンドがあるが、そこまでする必要がない場合 kill -SIGCONT $(pgrep hoge)
```
curlのリクエストとpythonファイルサーバからのレスポンスを確認することができる
``` bash ### あるプロセスが現在実行されているCPUのidを知りたい
# temirnal A
python3 -m http.server 8080 `trace-cmd`を利用する
# terminal B [trace cmd · Wiki · umaumax / memo · GitLab](https://gitlab.com/umaumax/memo/-/wikis/trace-cmd#cpu%E3%81%AE%E5%88%87%E3%82%8A%E6%9B%BF%E3%82%8F%E3%82%8A%E3%81%AE%E7%9B%A3%E8%A6%96)
socat -v TCP4-LISTEN:18080,fork TCP4:localhost:8080
### コンテキストスイッチの切り替え回数をn秒ごとに監視する
# terminal C
curl localhost:18080 下記の`cswch/s nvcswch/s`の項目を見る
```
```bash
``` bash pidstat -w 1 -p $PID
$ socat -v TCP4-LISTEN:18080,fork TCP4:localhost:8080 ```
> 2023/11/20 13:16:34.640853 length=78 from=0 to=77
GET / HTTP/1.1\r ### 特定のパスのコマンドを置き換える方法
Host: localhost:18080\r
User-Agent: curl/8.1.2\r * 方法1: `$PATH`を置き換える
Accept: */*\r * オリジナルのシェル関数(`set_command_logger COMMAND_NAME`)がある
\r * 方法2: `mount --bind`を利用する
< 2023/11/20 13:16:34.643186 length=155 from=0 to=154 * 直接ファイルパス指定で起動するケースでも置換可能
HTTP/1.0 200 OK\r * ただし、元のファイルが呼べなくなる
Server: SimpleHTTP/0.6 Python/3.9.12\r
Date: Mon, 20 Nov 2023 04:16:34 GMT\r ### g++コマンドの引数のトレース
Content-type: text/html; charset=utf-8\r
Content-Length: 297\r `cxx.sh`
\r
< 2023/11/20 13:16:34.643715 length=297 from=155 to=451 ```bash
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> #!/usr/bin/env bash
<html> set -x
<head> g++ "$@" -g3 -O0
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> ```
<title>Directory listing for /</title>
</head> オプションを強制的に追加することもできる
<body>
<h1>Directory listing for /</h1> ## ネットワーク
<hr>
<ul> ### TCP通信の中身を気軽に確認したい
</ul>
<hr> 詳しく確認するにはtcpdumpコマンドがあるが、そこまでする必要がない場合
</body>
</html> curlのリクエストとpythonファイルサーバからのレスポンスを確認することができる
```
```bash
### サーバアクセスのデバッグ # temirnal A
#### サーバからの応答がなく、永久待機状態となった python3 -m http.server 8080
1. サーバの起動
2. (この時点で、ポートへのアクセスを受け付け始める) # terminal B
3. 特定のパス(e.g. `/health`)の待受が完了する socat -v TCP4-LISTEN:18080,fork TCP4:localhost:8080
* __上記の2番の状態でクライアントが接続している場合に、ソケットのタイムアウト設定がない場合には永久に待機状態となる場合があることに注意__ # terminal C
* タイミング問題であるので、サーバが完全に起動した後でデバッグをしても、気が付かないことに注意 curl localhost:18080
* 仮説としては、3番まで起動しているが、`keep-alive`の影響で永久待機状態となっていると思いながらデバッグしていた ```
## 目的別 ```bash
### 特定の共有ファイルを利用しているプロセス一覧 $ socat -v TCP4-LISTEN:18080,fork TCP4:localhost:8080
``` bash > 2023/11/20 13:16:34.640853 length=78 from=0 to=77
lsof /lib/x86_64-linux-gnu/libnss_files-2.23.so GET / HTTP/1.1\r
Host: localhost:18080\r
ps $(grep /lib/x86_64-linux-gnu/libnss_files-2.23.so /proc/*/maps | cut -d / -f3 | sort -u) User-Agent: curl/8.1.2\r
``` Accept: */*\r
\r
### 遅延読み込みのライブラリが今現在、読み込まれているかどうかを知りたい < 2023/11/20 13:16:34.643186 length=155 from=0 to=154
`pmap`コマンドや`/proc/$PID/maps`を利用すれば、どのライブラリがどこのメモリマップにあるかわかる HTTP/1.0 200 OK\r
Server: SimpleHTTP/0.6 Python/3.9.12\r
### スタック破壊を検出したい Date: Mon, 20 Nov 2023 04:16:34 GMT\r
* stack protectorはリリース向けでも適用可能である(オーバーヘッドがほぼないため、リリース向けで実績あり) Content-type: text/html; charset=utf-8\r
* 破壊された場合には`assert`となるが、そもそも、スタック破壊された場合には予期しない挙動となるので、`assert`で処理が終了するほうがまだよい挙動である Content-Length: 297\r
* [Instrumentation Options (Using the GNU Compiler Collection (GCC))]( https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html ) \r
* `CFLAGS += -D_FORTIFY_SOURCE=1 -fstack-protector-strong` < 2023/11/20 13:16:34.643715 length=297 from=155 to=451
* [全てのプログラムに gcc の -D_FORTIFY_SOURCE と -fstack-protector オプションが効くわけではない話 | かわうそ通信]( https://konpeitoh.net/tech/stack-protector_20220724/ ) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
## 各スレッドがどのようなスレッドかどうかを知りたい <head>
### あるプロセスの各スレッドで実行した動的リンクした関数の一覧を取得して,図示したい <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
e.g. `xargs echo <<(ls)` </head>
``` bash <body>
function dot_wrapper() { <h1>Directory listing for /</h1>
awk ' <hr>
BEGIN { printf "digraph G {\n" } <ul>
{ </ul>
printf "\"%s\" -> \"%s\"\n", $1, $2; <hr>
} </body>
END { printf "}\n" } </html>
' ```
}
### サーバアクセスのデバッグ
ltrace -f xargs echo <<(ls) |& sort | sed -E 's/\[|\]|[()]/ /g' | grep pid | awk '{printf "%d %s\n",$2,$3; }' | grep -v '+++\|---\|<...' | sort | uniq > func.log
cat func.log | dot_wrapper | dot -Tsvg -o output.svg #### サーバからの応答がなく、永久待機状態となった
```
1. サーバの起動
### LD_PRELOADでPIDとスレッド生成時のバックトレースを表示する 2. (この時点で、ポートへのアクセスを受け付け始める)
[umaumax/pthreadinfo]( https://github.com/umaumax/pthreadinfo ) 3. 特定のパス(e.g. `/health`)の待受が完了する
`pthread_create`を呼び出したときのバックトレースを出力するツール * **上記の2番の状態でクライアントが接続している場合に、ソケットのタイムアウト設定がない場合には永久に待機状態となる場合があることに注意**
* タイミング問題であるので、サーバが完全に起動した後でデバッグをしても、気が付かないことに注意
### 後からすでに動作中のスレッドのバックトレースを取得する * 仮説としては、3番まで起動しているが、`keep-alive`の影響で永久待機状態となっていると思いながらデバッグしていた
``` bash ## 目的別
sudo gstack $PID
``` ### 特定の共有ファイルを利用しているプロセス一覧
内部的には、`gdb`を利用しているとのこと ```bash
lsof /lib/x86_64-linux-gnu/libnss_files-2.23.so
### すべてのスレッドのbtをdumpする例
``` bash ps $(grep /lib/x86_64-linux-gnu/libnss_files-2.23.so /proc/*/maps | cut -d / -f3 | sort -u)
cat << EOF | gdb -q -p $PID ```
set pagination off
set logging file ./gdb.log ### 遅延読み込みのライブラリが今現在、読み込まれているかどうかを知りたい
set logging on
thread apply all bt `pmap`コマンドや`/proc/$PID/maps`を利用すれば、どのライブラリがどこのメモリマップにあるかわかる
EOF
``` ### スタック破壊を検出したい
この結果のdiffをとれば、概ね停止しているスレッドのbtは一致するのでどのスレッドが実際に動作しているのかのイメージが掴める * stack protectorはリリース向けでも適用可能である(オーバーヘッドがほぼないため、リリース向けで実績あり)
* 破壊された場合には`assert`となるが、そもそも、スタック破壊された場合には予期しない挙動となるので、`assert`で処理が終了するほうがまだよい挙動である
or * [Instrumentation Options (Using the GNU Compiler Collection (GCC))](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html)
* `CFLAGS += -D_FORTIFY_SOURCE=1 -fstack-protector-strong`
``` bash * [全てのプログラムに gcc の -D_FORTIFY_SOURCE と -fstack-protector オプションが効くわけではない話 | かわうそ通信](https://konpeitoh.net/tech/stack-protector_20220724/)
PID=XXX ; while true; do sleep 0.01; { date +%s%3N; cat /proc/$PID/task/*/stat | awk -v SC_CLK_TCK=100 '{ tid=$1; state=$3; print tid, state; }'; } | tee -a ps.log; done | grep -v S
``` ## 各スレッドがどのようなスレッドかどうかを知りたい
として定期的に情報をdumpすると、動いているスレッドがわかる
### あるプロセスの各スレッドで実行した動的リンクした関数の一覧を取得して,図示したい
``` bash
git diff --word-diff --no-index -- gdb1.log gdb2.log e.g. `xargs echo <<(ls)`
```
```bash
## ビルド function dot_wrapper() {
### バージョン違いによる不具合の具体例 awk '
ターゲットAでビルドしたものをターゲットBで利用するとき(クロスビルドでなくてもよい),例えば,dockerでビルドしたrosパッケージをホストマシンで実行するときに、`apt-get`で入れているライブラリ(`ros`)のバージョンがinstallした時期によって異なるので,`sudo apt list --upgradable`として確認するとよい BEGIN { printf "digraph G {\n" }
{
### ソースからビルドする際の注意点 printf "\"%s\" -> \"%s\"\n", $1, $2;
githubでtagを利用してcheckoutする場合とリリースファイルを利用する場合で内容が変化することがあり、基本的にはリリース物を利用する手順が正しい }
(意外とこのケースに遭遇し、リリースファイルを利用するとビルドエラーなどの問題が解決することが多い) END { printf "}\n" }
'
e.g. [Build fail on Ubuntu 18\.04 x86\_64 · Issue \#35 · twistedfall/opencv\-rust]( https://github.com/twistedfall/opencv-rust/issues/35 ) }
### 異なる環境でビルドした成果物が同等であるかどうかの妥当性の検証をしたい ltrace -f xargs echo <<(ls) |& sort | sed -E 's/\[|\]|[()]/ /g' | grep pid | awk '{printf "%d %s\n",$2,$3; }' | grep -v '+++\|---\|<...' | sort | uniq > func.log
cat func.log | dot_wrapper | dot -Tsvg -o output.svg
``` sh ```
find . -name "*.so" | xargs -n1 md5sum
find . -name "*.so" | xargs -n1 -I{} bash -c "echo {}; strings {} | md5sum" ### LD_PRELOADでPIDとスレッド生成時のバックトレースを表示する
```
[umaumax/pthreadinfo](https://github.com/umaumax/pthreadinfo)
* 実際に、異なる環境でビルドした結果でも上記のように`strings`を通すと一致する場合があったが、これは、あくまでも、デバッグの目安にしかならず、確実性はないことに注意
* `strings`を行う前に`strip`をかませるとよいかもしれない(`strip`は指定したファイル自体に変更を加えることに注意) `pthread_create`を呼び出したときのバックトレースを出力するツール
バイナリファイルかどうかは`-executable`の他に拡張子が`.sh`であるファイルも対象になることに注意 ### 後からすでに動作中のスレッドのバックトレースを取得する
### buildできない or 意図しない実行結果となる ```bash
* `git checkout`をした後にそのままbuildした場合 sudo gstack $PID
* `git rebase`をした後にそのままbuildした場合 ```
上記,依存関係の設定が不完全な`make`でよく起こり得る 内部的には、`gdb`を利用しているとのこと
### バイナリコマンドの差分検出 ### すべてのスレッドのbtをdumpする例
#### どうしても、データが異なる場合の説明?
バイナリデータのアライメントを揃える目的のダミーデータの可能性は? ```bash
cat << EOF | gdb -q -p $PID
#### バイナリ差分の比較手順の考察 set pagination off
* `strip -s`コマンドでヘッダ情報を削除する set logging file ./gdb.log
* `objdump -x`or`objdump -d`でdiffをとる set logging on
thread apply all bt
#### 既存ツール EOF
[elfdiff \- マニュアルページ セクション 1: ユーザーコマンド]( https://docs.oracle.com/cd/F16635_01/html/E71065/elfdiff-1.html ) ```
Oracle Solarisのmanualがあるが実装は簡単に見れる範囲ではweb上では見つからない(OSをダウンロードすれば見れそうだが) この結果のdiffをとれば、概ね停止しているスレッドのbtは一致するのでどのスレッドが実際に動作しているのかのイメージが掴める
[elfdiff \- man pages section 1: User Commands]( https://docs.oracle.com/cd/E88353_01/html/E37839/elfdiff-1.html ) or
特に比較したいセクション ```bash
PID=XXX ; while true; do sleep 0.01; { date +%s%3N; cat /proc/$PID/task/*/stat | awk -v SC_CLK_TCK=100 '{ tid=$1; state=$3; print tid, state; }'; } | tee -a ps.log; done | grep -v S
> Sections that contain program data, whose interpretation is meaningful only to the application. These sections include the program .text instructions and the associated data sections such as .rodata and .data. ```
> Sections that contain link-editing information, such as the symbol table information found in the .symtab and .strtab sections, and relocation information such as the .rela.text section. として定期的に情報をdumpすると、動いているスレッドがわかる
[mvertescher/elfdiff: Simple ELF difftool]( https://github.com/mvertescher/elfdiff ) ```bash
rust実装で、やっていることは同じ git diff --word-diff --no-index -- gdb1.log gdb2.log
```
[dnezamaev/pyelfcmp: Deep comparing ELF files in Python]( https://github.com/dnezamaev/pyelfcmp )
python実装で、やっていることは同じ ## ビルド
[CapeLeidokos/elf\_diff: A tool to compare ELF binaries]( https://github.com/CapeLeidokos/elf_diff ) ### バージョン違いによる不具合の具体例
python実装で、比較項目がバイナリデータのdiffではなく、各セクション数などであるので出力形式が異なる
ターゲットAでビルドしたものをターゲットBで利用するとき(クロスビルドでなくてもよい),例えば,dockerでビルドしたrosパッケージをホストマシンで実行するときに、`apt-get`で入れているライブラリ(`ros`)のバージョンがinstallした時期によって異なるので,`sudo apt list --upgradable`として確認するとよい
[abidiff\(1\) \- Linux manual page]( https://man7.org/linux/man-pages/man1/abidiff.1.html )
対象ファイルが共有ライブラリのみのdiffかつ比較項目がバイナリデータのdiffではなく、関数や変数の数などであるので出力形式が異なる ### ソースからビルドする際の注意点
abidiff githubでtagを利用してcheckoutする場合とリリースファイルを利用する場合で内容が変化することがあり、基本的にはリリース物を利用する手順が正しい (意外とこのケースに遭遇し、リリースファイルを利用するとビルドエラーなどの問題が解決することが多い)
``` bash
sudo apt-get install abigail-tools e.g. [Build fail on Ubuntu 18.04 x86_64 · Issue #35 · twistedfall/opencv-rust](https://github.com/twistedfall/opencv-rust/issues/35)
```
### 異なる環境でビルドした成果物が同等であるかどうかの妥当性の検証をしたい
## トラブルシューティング
### 🔥html/css/javascriptファイルの変更が反映されない ```sh
ウェブブラウザの更新時に通常ロードではなく、サーバからデータを強制的に取得する`ハードロード`を実施すること(Macでは`Command+Shift+R`) find . -name "*.so" | xargs -n1 md5sum
find . -name "*.so" | xargs -n1 -I{} bash -c "echo {}; strings {} | md5sum"
### ログインできない ```
パスワード入力時に特に、ユーザ名の先頭にスペースが入り込んでいると見た目で判断しにくいことに注意
(特に、ディスプレイを反応させるために、スペースキーを打った時など) * 実際に、異なる環境でビルドした結果でも上記のように`strings`を通すと一致する場合があったが、これは、あくまでも、デバッグの目安にしかならず、確実性はないことに注意
* `strings`を行う前に`strip`をかませるとよいかもしれない(`strip`は指定したファイル自体に変更を加えることに注意)
### 外部と通信する処理での意図しないエラー
クライアントマシンの時刻があっているかどうかを確認すること バイナリファイルかどうかは`-executable`の他に拡張子が`.sh`であるファイルも対象になることに注意
### ビルドで差分が適用されない ### buildできない or 意図しない実行結果となる
NTPサーバは正しく設定すること
* `git checkout`をした後にそのままbuildした場合
少なくとも、時間がずれていてもよいが,時間がジャンプしていないことを確認すること * `git rebase`をした後にそのままbuildした場合
そうしないと,`make`で正しく差分ビルドされなくなることに注意 上記,依存関係の設定が不完全な`make`でよく起こり得る
### ウェブからのコピペしたコマンドの動作がおかしい(スペースが怪しい場合) ### バイナリコマンドの差分検出
[C2A0問題を解決する]( https://lab.unicast.ne.jp/2013/10/22/fix-c2a0-problem/ )
#### どうしても、データが異なる場合の説明?
### セットアップツールなどのGUIとCUI両対応コマンドをCUI想定で起動した際にハングアップしている用に見える問題
これはGUIで待機状態となっているために、おかしな状況に見えるので、特にリモート作業中はこれに注意すること バイナリデータのアライメントを揃える目的のダミーデータの可能性は?
しかし、この場合、GUIで勧めていくとハングアップする経験が多いので、基本的にはCUIをおすすめする #### バイナリ差分の比較手順の考察
### `sudo passwd`だとroot userの設定をすることになるので、注意 * `strip -s`コマンドでヘッダ情報を削除する
``` bash * `objdump -x`or`objdump -d`でdiffをとる
sudo passwd $USER
``` #### 既存ツール
### プロセスがたまに停止しているようにみえる場合 [elfdiff - マニュアルページ セクション 1: ユーザーコマンド](https://docs.oracle.com/cd/F16635_01/html/E71065/elfdiff-1.html)
HWの割り込み処理などはCPU0に集中するような設定になっていることが多いが関連があるかもしれない
`cat /proc/interrupts`で確認できる Oracle Solarisのmanualがあるが実装は簡単に見れる範囲ではweb上では見つからない(OSをダウンロードすれば見れそうだが)
上記の割り込みをよく利用するプロセスが時々停止してしまうことがある場合にはtasksetを利用してCPU0に余計なプロセスを割り当てないアフィニティとするか、割り込みを他のCPUでも許可する?(未検証) [elfdiff - man pages section 1: User Commands](https://docs.oracle.com/cd/E88353_01/html/E37839/elfdiff-1.html)
### pkillの挙動が奇妙な場合 特に比較したいセクション
* 他のユーザや自分が`tmux`経由でログインしているときに、プロセスが動いているままの可能性はないか?
* 特に、あるプログラムを動かして、一定時間後に`pkill`する場合に、他のユーザの権限で動作しているプログラムの方が若いPIDだと、`pkill`の処理がそこで止まる疑惑がある => いや,signal自体はすべての該当プロセスに飛ぶので,これは関係ない > Sections that contain program data, whose interpretation is meaningful only to the application. These sections include the program .text instructions and the associated data sections such as .rodata and .data.
### 特定の実行ファイルを決まった場所に配置する運用で、`Text file busy`を回避したい > Sections that contain link-editing information, such as the symbol table information found in the .symtab and .strtab sections, and relocation information such as the .rela.text section.
予め、シンボリックリンクで実行ファイルを参照する形式にしておいて、その参照先を差し替える形式とすればよい
[mvertescher/elfdiff: Simple ELF difftool](https://github.com/mvertescher/elfdiff) rust実装で、やっていることは同じ
### `Makefile`が`makefile`となっているケースに注意
問題なくmakeコマンドで利用できるが、find時には注意 [dnezamaev/pyelfcmp: Deep comparing ELF files in Python](https://github.com/dnezamaev/pyelfcmp) python実装で、やっていることは同じ
### あるプロセスでbad_allocが発生した場合のデバッグ方法 [CapeLeidokos/elf_diff: A tool to compare ELF binaries](https://github.com/CapeLeidokos/elf_diff) python実装で、比較項目がバイナリデータのdiffではなく、各セクション数などであるので出力形式が異なる
* 長時間稼働するプロセスならば、外部から観測する
* 短い時間に、一気にメモリが利用される場合は外部からの観測では傾向がわからないので、内部から観測できるようにする [abidiff(1) - Linux manual page](https://man7.org/linux/man-pages/man1/abidiff.1.html) 対象ファイルが共有ライブラリのみのdiffかつ比較項目がバイナリデータのdiffではなく、関数や変数の数などであるので出力形式が異なる
例えば、`std::bad_alloc`の例外を補足して、プロセスを停止すれば、その時点の`/proc/buddyinfo`などを観測できる abidiff
具体的な方法として、`LD_PRELOAD``new`を置き換えて、その中で、try catchして、`raise(SIGSTOP)`して停止している状態で`cat /proc/buddyinfo`を見る ```bash
sudo apt-get install abigail-tools
特にメモリが不足しているようには見えない場合は、不正なサイズで巨大なメモリ確保が起きている可能性が考えられる(特に、未初期化領域の数値を参照したサイズを確保しているケースが考えられる) ```
### SEGVしなさそうな場所でSEGVした場合のデバッグ ## トラブルシューティング
伝播(でんぱ)した結果SEGVした可能性があるので、N個前に未初期化領域にアクセスしていないかどうかなどを見ること
### :fire:html/css/javascriptファイルの変更が反映されない
例えば、未初期化領域のメソッドを呼んだ場合など
ウェブブラウザの更新時に通常ロードではなく、サーバからデータを強制的に取得する`ハードロード`を実施すること(Macでは`Command+Shift+R`)
``` cpp
class Hoge { ### ログインできない
public:
void NotifyOne() { condition_variable_.notify_one(); } パスワード入力時に特に、ユーザ名の先頭にスペースが入り込んでいると見た目で判断しにくいことに注意 (特に、ディスプレイを反応させるために、スペースキーを打った時など)
private: ### 外部と通信する処理での意図しないエラー
std::condition_variable condition_variable_;
}; クライアントマシンの時刻があっているかどうかを確認すること
int main() {
std::shared_ptr<Hoge> hoge; ### ビルドで差分が適用されない
hoge->NotifyOne();
return 0; NTPサーバは正しく設定すること
}
``` 少なくとも、時間がずれていてもよいが,時間がジャンプしていないことを確認すること
``` そうしないと,`make`で正しく差分ビルドされなくなることに注意
#0 pthread_cond_signal@@GLIBC_2.3.2 () at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_signal.S:39
#1 0x00007ffff7b08939 in std::condition_variable::notify_one() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ### ウェブからのコピペしたコマンドの動作がおかしい(スペースが怪しい場合)
#2 0x00000000004009a8 in Hoge::NotifyOne() ()
#3 0x000000000040090f in main () [C2A0問題を解決する](https://lab.unicast.ne.jp/2013/10/22/fix-c2a0-problem/)
```
### セットアップツールなどのGUIとCUI両対応コマンドをCUI想定で起動した際にハングアップしている用に見える問題
### 意図しないSEGV
想定と異なるバージョンのheaderや共有ライブラリを組み合わせて利用してしまっている可能性がある これはGUIで待機状態となっているために、おかしな状況に見えるので、特にリモート作業中はこれに注意すること
特に、システムとローカルの両方にライブラリがある場合になりやすい`gtest`などで発生しがち しかし、この場合、GUIで勧めていくとハングアップする経験が多いので、基本的にはCUIをおすすめする
### 意図しない挙動 ### `sudo passwd`だとroot userの設定をすることになるので、注意
* `printf`でのフォーマット間違いは意図しない挙動を引き起こす可能性が非常に高いことに注意
* 予想不能な不可解な場合は個のケースが多い ```bash
* 特に、intの型やStringに対して`c_str()`を使わずに、`%s`とするパターンが多い sudo passwd $USER
```
### 例外が発生した箇所(try-catchしている場合を含む)のスタックトレースを取得したい
[tnakagome/exray: Show stack traces from C\+\+ exceptions at run\-time without rebuilding target applications]( https://github.com/tnakagome/exray ) ### プロセスがたまに停止しているようにみえる場合
`LD_PRELOAD`を利用することで、リビルドが不要なツール(まだ、試したことはない) HWの割り込み処理などはCPU0に集中するような設定になっていることが多いが関連があるかもしれない `cat /proc/interrupts`で確認できる
### CPUの負荷が高いときに注意するプロセス 上記の割り込みをよく利用するプロセスが時々停止してしまうことがある場合にはtasksetを利用してCPU0に余計なプロセスを割り当てないアフィニティとするか、割り込みを他のCPUでも許可する?(未検証)
* neovim経由で実行するプラグイン系のプロセス
* `semgrep-core` ### pkillの挙動が奇妙な場合
* LSPデーモン
* tmuxプロセスの暴走 * 他のユーザや自分が`tmux`経由でログインしているときに、プロセスが動いているままの可能性はないか?
* Macの`pboard`プロセスが暴走するとGUIが固まる * 特に、あるプログラムを動かして、一定時間後に`pkill`する場合に、他のユーザの権限で動作しているプログラムの方が若いPIDだと、`pkill`の処理がそこで止まる疑惑がある =\> いや,signal自体はすべての該当プロセスに飛ぶので,これは関係ない
* ほかマシンからのsshは可能
### 特定の実行ファイルを決まった場所に配置する運用で、`Text file busy`を回避したい
## 負荷をかけたい
### CPU 予め、シンボリックリンクで実行ファイルを参照する形式にしておいて、その参照先を差し替える形式とすればよい
``` bash
yes > /dev/null ### `Makefile`が`makefile`となっているケースに注意
```
問題なくmakeコマンドで利用できるが、find時には注意
stressコマンド
### あるプロセスでbad_allocが発生した場合のデバッグ方法
[simple stress command]( https://gist.github.com/umaumax/7c9018a60a001bfc919afda7003bf032 )
* 長時間稼働するプロセスならば、外部から観測する
### メモリ使用率 * 短い時間に、一気にメモリが利用される場合は外部からの観測では傾向がわからないので、内部から観測できるようにする
``` bash
/dev/null < $(yes) 例えば、`std::bad_alloc`の例外を補足して、プロセスを停止すれば、その時点の`/proc/buddyinfo`などを観測できる
```
具体的な方法として、`LD_PRELOAD``new`を置き換えて、その中で、try catchして、`raise(SIGSTOP)`して停止している状態で`cat /proc/buddyinfo`を見る
実行しているbashやzshなどのプロセスの`VIRT``RES`が上がり、`zsh: fatal error: out of heap memory`となって、プロセスが落ちる
特にメモリが不足しているようには見えない場合は、不正なサイズで巨大なメモリ確保が起きている可能性が考えられる(特に、未初期化領域の数値を参照したサイズを確保しているケースが考えられる)
## 単体テスト
### たまに失敗する場合の原因 ### SEGVしなさそうな場所でSEGVした場合のデバッグ
* 時間が関わっているのでは?
* 短時間の連続リクエストの拒否ロジックなど 伝播(でんぱ)した結果SEGVした可能性があるので、N個前に未初期化領域にアクセスしていないかどうかなどを見ること
* 非同期APIの結果待ちなど
* スレッド起動後の処理タイミングが想定と異なる(テスト特有の立ち上げてからすぐに終了するようなケース) 例えば、未初期化領域のメソッドを呼んだ場合など
* e.g. condition variableのwaitが行われる前にnotifyが行われ、ずっとwaitし続けるなど
```cpp
## 再現 class Hoge {
### gdb実行時にSEGVなどが再現しなくなる場合は、普通に実行し、coredumpする設定とすれば良い public:
void NotifyOne() { condition_variable_.notify_one(); }
## 読み物
### [Linuxのloadavgが約7時間ごとに上昇する現象の原因 \- Mackerel お知らせ \#mackerelio]( https://mackerel.io/ja/blog/entry/tech/high-loadavg-every-7-hours ) private:
\ No newline at end of file std::condition_variable condition_variable_;
};
int main() {
std::shared_ptr<Hoge> hoge;
hoge->NotifyOne();
return 0;
}
```
```
#0 pthread_cond_signal@@GLIBC_2.3.2 () at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_signal.S:39
#1 0x00007ffff7b08939 in std::condition_variable::notify_one() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#2 0x00000000004009a8 in Hoge::NotifyOne() ()
#3 0x000000000040090f in main ()
```
### 意図しないSEGV
想定と異なるバージョンのheaderや共有ライブラリを組み合わせて利用してしまっている可能性がある
特に、システムとローカルの両方にライブラリがある場合になりやすい`gtest`などで発生しがち
### 意図しない挙動
* `printf`でのフォーマット間違いは意図しない挙動を引き起こす可能性が非常に高いことに注意
* 予想不能な不可解な場合は個のケースが多い
* 特に、intの型やStringに対して`c_str()`を使わずに、`%s`とするパターンが多い
### 例外が発生した箇所(try-catchしている場合を含む)のスタックトレースを取得したい
[tnakagome/exray: Show stack traces from C++ exceptions at run-time without rebuilding target applications](https://github.com/tnakagome/exray)
`LD_PRELOAD`を利用することで、リビルドが不要なツール(まだ、試したことはない)
### CPUの負荷が高いときに注意するプロセス
* neovim経由で実行するプラグイン系のプロセス
* `semgrep-core`
* LSPデーモン
* tmuxプロセスの暴走
* Macの`pboard`プロセスが暴走するとGUIが固まる
* ほかマシンからのsshは可能
## 負荷をかけたい
### CPU
```bash
yes > /dev/null
```
stressコマンド
[simple stress command](https://gist.github.com/umaumax/7c9018a60a001bfc919afda7003bf032)
### メモリ使用率
```bash
/dev/null < $(yes)
```
実行しているbashやzshなどのプロセスの`VIRT``RES`が上がり、`zsh: fatal error: out of heap memory`となって、プロセスが落ちる
## 単体テスト
### たまに失敗する場合の原因
* 時間が関わっているのでは?
* 短時間の連続リクエストの拒否ロジックなど
* 非同期APIの結果待ちなど
* スレッド起動後の処理タイミングが想定と異なる(テスト特有の立ち上げてからすぐに終了するようなケース) \* e.g. condition variableのwaitが行われる前にnotifyが行われ、ずっとwaitし続けるなど
## 再現
### gdb実行時にSEGVなどが再現しなくなる場合は、普通に実行し、coredumpする設定とすれば良い
## 読み物
### [Linuxのloadavgが約7時間ごとに上昇する現象の原因 - Mackerel お知らせ #mackerelio](https://mackerel.io/ja/blog/entry/tech/high-loadavg-every-7-hours)
\ No newline at end of file