コードのデバッグ#
プリントデバッグ法とログ#
- ログをファイル、ソケット、またはリモートサーバーに送信することができ、標準出力だけに限りません;
- ログは重大度レベル(例:INFO、DEBUG、WARN、ERROR など)をサポートしており、必要に応じてログをフィルタリングできます;
- 新たに発見された問題については、ログに問題の特定に役立つ十分な情報が含まれている可能性が高いです。
サードパーティのログシステム#
- Unix
/var/logdmesg - Linux
systemd/var/log/journaljournalctl - MacOS
/var/log/system.loglog show loggerシステムログにログを書き込む
logger "Hello Logs"
# macOSで
log show --last 1m | grep Hello
# Linuxで
journalctl --since "1m ago" | grep Hello
lnavログビューア、より良い表示とブラウジング方法
デバッガ#
ipdb 拡張型 pdb Python デバッガ
- l(ist) - 現在の行の近くの 11 行を表示するか、以前の表示を続行します;
- s(tep) - 現在の行を実行し、最初の可能な場所で停止します;
- n(ext) - 現在の関数の次の文または return 文まで実行を続けます;
- b(reak) - ブレークポイントを設定します(渡された引数に基づいて);
- p(rint) - 現在のコンテキストで式を評価し、結果を印刷します。もう一つのコマンドはppで、
pprintを使用して印刷します; - r(eturn) - 現在の関数が返るまで実行を続けます;
- q(uit) - デバッガを終了します。
より低レベルのプログラミング言語については、gdb(およびその改良版pwndbg)やlldbについて知っておく必要があります。
これらはすべて C 言語系のデバッグに最適化されており、任意のプロセスとそのマシン状態(レジスタ、スタック、プログラムカウンタなど)を探索することを可能にします。
専用ツール#
プログラムの実行を追跡するシステムコール
- Linux
strace - MacOS BSD
dtrace、dtrussでラップしてstraceに似たインターフェースを持たせます
# Linuxで
sudo strace -e lstat ls -l > /dev/null
4
# macOSで
sudo dtruss -t lstat64_extended ls -l > /dev/null
ネットワークパケット分析ツール: tcpdump Wireshark
Chrome/Firefox の開発者ツール
- ソースコード - 任意のサイトの HTML/CSS/JS ソースコードを表示;
- HTML、CSS、JS コードをリアルタイムで変更 - ウェブサイトの内容、スタイル、動作をテストのために変更(この点からも、ウェブページのスクリーンショットは信頼できないことがわかります);
- Javascript シェル - JS REPL でコマンドを実行;
- ネットワーク - リクエストのタイムラインを分析;
- ストレージ - Cookies とローカルアプリケーションストレージを表示。
静的分析#
プログラムのソースコードを入力として、コーディングルールに基づいて分析し、コードの正確性を推論します。
Python: pyflakes mypy
シェルスクリプト:shellcheck
コードリント スタイルチェックまたはセキュリティチェック
Vim: ale syntastic
Python: pylint pep8 スタイルチェック bandit セキュリティチェック
他の言語の開発者向けには、静的分析ツールのリストを参考にできます:Awesome Static Analysis(_Writing_セクションに興味があるかもしれません)。リンターについてはこのリストを参考にしてください:Awesome Linters。
パフォーマンス分析#
タイミング#
- 実時間 - プログラムの開始から終了までの実際の時間、他のプロセスの実行時間やブロッキングにかかる時間(例:I/O やネットワーク待機を含む);
- User - CPU がユーザーコードを実行するのにかかる時間;
- Sys - CPU がシステムカーネルコードを実行するのにかかる時間。
$ time curl https://missing.csail.mit.edu &> /dev/null
real 0m2.561s
user 0m0.015s
sys 0m0.012s
パフォーマンス分析ツール(プロファイラ)#
CPU#
CPU パフォーマンス分析ツールには、トレースアナライザ(tracing)とサンプリングアナライザ(sampling)の 2 種類があります。トレースアナライザはプログラムの各関数呼び出しを記録し、サンプリングアナライザは通常は毎ミリ秒ごとにプログラムを監視し、プログラムスタックを記録します。
メモリ#
C や C++ のような言語では、メモリリークが発生すると、プログラムがメモリを使い終わった後にそれを解放しません。メモリ関連のバグに対処するために、Valgrindのようなツールを使用してメモリリークの問題をチェックできます。
Python のようなガーベジコレクション機構を持つ言語では、メモリアナライザも非常に役立ちます。なぜなら、あるオブジェクトに対してポインタがまだ指している限り、それは回収されないからです。
イベント分析#
straceを使用してコードをデバッグしているとき、特定のコードを無視したい場合や、分析時にそれをブラックボックスとして扱いたい場合があります。perfコマンドは CPU の違いを抽象化し、時間やメモリの消費を報告するのではなく、プログラムに関連するシステムイベントを報告します。
例えば、perfは悪いキャッシュ局所性(poor cache locality)、大量のページフォールト(page faults)やライブロック(livelocks)を報告できます。以下は一般的なコマンドの概要です:
perf list- pref が追跡できるイベントをリストします;perf stat COMMAND ARG1 ARG2- 特定のプロセスや命令に関連するイベントを収集します;perf record COMMAND ARG1 ARG2- コマンド実行のサンプリング情報を記録し、統計データをperf.dataに保存します;perf report-perf.dataのデータをフォーマットして印刷します。
可視化#
実際のプログラムを分析するためにアナライザを使用すると、ソフトウェアの複雑性により、その出力結果には大量の情報が含まれます。人間は視覚的な生き物であり、大量のテキストを読むのが得意ではありません。そのため、多くのツールは可視化されたアナライザ出力結果の機能を提供しています。
サンプリングアナライザの場合、CPU 分析データを表示する一般的な形式はフレームグラフです。フレームグラフは Y 軸に関数呼び出しの関係を表示し、X 軸にその時間の割合を表示します。フレームグラフはインタラクティブでもあり、プログラムの特定の部分に深く入り込み、そのスタックトレースを確認できます。
呼び出しグラフと制御フローグラフは、サブプログラム間の関係を表示します。関数をノードとして、関数呼び出しをエッジとして表示します。これらをアナライザの情報(例えば呼び出し回数、時間など)と組み合わせて使用すると、呼び出しグラフは非常に役立ちます。Python では、pycallgraphを使用してこれらの画像を生成できます。
リソース監視#
- 一般的な監視 - 最も人気のあるツールは
htopで、これはtopの改良版です。htopは現在実行中のプロセスのさまざまな統計情報を表示できます。htopには多くのオプションとショートカットキーがあり、一般的なものには:<F6>プロセスのソート、tツリー構造の表示、hスレッドの開閉があります。glancesも注目に値します。これは実装が似ていますが、ユーザーインターフェースがより良好です。すべてのプロセスの測定を統合する必要がある場合、dstatも非常に便利なツールで、I/O、ネットワーク、CPU 使用率、コンテキストスイッチなど、さまざまなサブシステムリソースの測定データをリアルタイムで計算できます; - I/O 操作 -
iotopはリアルタイムの I/O 使用情報を表示し、特定のプロセスが大量のディスク読み書きを実行しているかどうかを簡単に確認できます; - ディスク使用 -
dfは各パーティションの情報を表示し、duは現在のディレクトリ内の各ファイルのディスク使用状況(disk usage)を表示します。-hオプションを使用すると、コマンドが人間(human)により優しい形式でデータを表示します;ncduはよりインタラクティブなduで、異なるディレクトリをナビゲートしたり、ファイルやフォルダを削除したりできます; - メモリ使用 -
freeはシステムの現在の空きメモリを表示します。メモリもhtopのようなツールを使用して表示できます; - オープンファイル -
lsofはプロセスによってオープンされているファイルの情報をリストします。特定のファイルがどのプロセスによってオープンされているかを確認する必要があるとき、このコマンドは非常に便利です; - ネットワーク接続と設定 -
ssはネットワークパケットの送受信状況やネットワークインターフェースの表示情報を監視するのに役立ちます。ssの一般的な使用シーンは、ポートがプロセスによって占有されている情報を見つけることです。ルーティング、ネットワークデバイス、インターフェース情報を表示するには、ipコマンドを使用できます。注意:netstatとifconfigの 2 つのコマンドは、前述のツールに置き換えられました。 - ネットワーク使用 -
nethogsとiftopは、ネットワーク使用を監視するための非常に良いインタラクティブコマンドラインツールです。
これらのツールをテストしたい場合、stressコマンドを使用してシステムに人工的に負荷をかけることができます。
専用ツール#
時には、ブラックボックスプログラムのベンチマークテストを行い、それに基づいてソフトウェアの選択を評価する必要があります。hyperfineのようなコマンドラインツールは、迅速にベンチマークテストを行うのに役立ちます。
デバッグと同様に、ブラウザにもページの読み込みを分析するための優れたパフォーマンス分析ツールが含まれており、時間がどこに消費されているのか(読み込み、レンダリング、スクリプトなど)を明らかにすることができます。より多くの情報は、FirefoxやChromeのリンクをクリックしてください。
課題#
デバッグ#
-
Linux の
journalctlまたは macOS のlog showコマンドを使用して、最近 1 日間のスーパーユーザーのログイン情報と実行したコマンドを取得します。関連情報が見つからない場合は、無害なコマンド(例:sudo ls)を実行してから再度確認してください。 -
shellcheckをインストールし、以下のスクリプトをチェックしてみてください。このコードには何か問題がありますか?関連する問題を修正してください。エディタにリンタープラグインをインストールして、自動的に関連する警告情報を表示できるようにします。#!/bin/sh ## 例:いくつかの問題を持つ典型的なスクリプト for f in $(ls *.m3u) do grep -qi hq.*mp3 $f \ && echo -e 'Playlist $f contains a HQ file in mp3 format' done
パフォーマンス分析#
-
ここにいくつかのソートアルゴリズムの実装があります。
cProfileとline_profilerを使用して、挿入ソートとクイックソートのパフォーマンスを比較してください。2 つのアルゴリズムのボトルネックはそれぞれどこですか?次に、memory_profilerを使用してメモリ消費をチェックし、なぜ挿入ソートがより良いのかを考えてみてください。その後、インプレースソートバージョンのクイックソートを確認してください。追加の質問:perfを使用して異なるアルゴリズムのループ回数とキャッシュヒット・ミスの状況を確認してください。 -
ここにフィボナッチ数列の計算のための Python コードがあります。各数字を計算するために関数を定義しています:
#!/usr/bin/env python def fib0(): return 0 def fib1(): return 1 s = """def fib{}(): return fib{}() + fib{}()""" if __name__ == '__main__': for n in range(2, 10): exec(s.format(n, n-1, n-2)) # from functools import lru_cache # for n in range(10): # exec("fib{} = lru_cache(1)(fib{})".format(n, n)) print(eval("fib9()"))コードをファイルにコピーして実行可能なプログラムにします。まず、
pycallgraphとgraphvizをインストールします(dotを実行できれば、GraphViz がインストールされています)。そして、pycallgraph graphviz -- ./fib.pyを使用してコードを実行し、pycallgraph.pngファイルを確認します。fib0は何回呼び出されましたか?メモ化を使用して最適化できます。コメントアウトされている部分を有効にして、再度画像を生成してください。今度は各fibN関数は何回呼び出されましたか? -
よくある状況として、リスニングしたいポートが他のプロセスによって占有されていることがあります。プロセスの PID を使用して関連するプロセスを見つけましょう。まず、
python -m http.server 4444を実行して、最もシンプルなウェブサーバーを起動し、4444ポートをリスニングします。別のターミナルで、lsof | grep LISTENを実行して、すべてのリスニングポートのプロセスと関連するポートを表示します。対応する PID を見つけて、kill <PID>を使用してそのプロセスを停止します。 -
プロセスリソースの制限も非常に有用な技術です。
stress -c 3を実行し、htopを使用して CPU 消費を可視化します。次に、taskset --cpu-list 0,2 stress -c 3を実行し、可視化します。stressは 3 つの CPU を占有していますか?なぜそうならないのでしょうか?man tasksetを読んで答えを探してください。追加の質問:cgroupsを使用して同じ操作を実現し、stress -mのメモリ使用を制限します。 -
(上級問題)
curl ipinfo.ioコマンドを実行して、IP に関する情報を取得します。curlが発起したリクエストと受信した応答メッセージをキャプチャするために、Wiresharkを開きます。(ヒント:httpを使用してフィルタリングし、HTTP メッセージのみを表示できます)