--format
パラメーターを使用して、ファイル形式を変更して、スピードスコープ プロファイルまたは生データを生成できます。サンプリング レートの変更、GIL を保持するスレッドのみを含めるフィルタリング、ネイティブ C 拡張機能のプロファイリング、スレッド ID の表示、サブプロセスのプロファイリングなど、その他のオプションの詳細についてはpy-spy record --help
を参照してください。
Top には、Unix の top コマンドと同様に、Python プログラム内で最も時間がかかっている関数のライブ ビューが表示されます。 py-spy を次のように実行します。
py-spy top --pid 12345
# OR
py-spy top -- python myprogram.py
Python プログラムのライブ更新の概要ビューが表示されます。
py-spy は、 dump
コマンドを使用して、各 Python スレッドの現在の呼び出しスタックを表示することもできます。
py-spy dump --pid 12345
これにより、各スレッドの呼び出しスタックとその他の基本的なプロセス情報がコンソールにダンプされます。
これは、Python プログラムがどこでハングしているかを把握するために 1 つの呼び出しスタックだけが必要な場合に便利です。このコマンドには、 --locals
フラグを設定することで、各スタック フレームに関連付けられたローカル変数を出力する機能もあります。
このプロジェクトは、プログラムが運用トラフィックを処理している場合でも、実行中の Python プログラムをプロファイリングおよびデバッグできるようにすることを目的としています。
他にも多くの Python プロファイリング プロジェクトがありますが、それらのほとんどすべてでは、プロファイリングされたプログラムを何らかの方法で変更する必要があります。通常、プロファイリング コードはターゲットの Python プロセス内で実行されるため、プログラムの速度が低下し、動作方法が変更されます。これは、これらのプロファイラーは通常、パフォーマンスに顕著な影響を与えるため、運用サービスの問題のデバッグにこれらのプロファイラーを使用するのは一般に安全ではないことを意味します。
py-spy は、Linux の process_vm_readv システム コール、OSX の vm_read コール、Windows の ReadProcessMemory コールを使用して、Python プログラムのメモリを直接読み取ることで機能します。
Python プログラムのコール スタックを把握するには、グローバル PyInterpreterState 変数を調べてインタープリタで実行されているすべての Python スレッドを取得し、各スレッドの各 PyFrameObject を反復処理してコール スタックを取得します。 Python ABI はバージョン間で異なるため、Rust の bindinggen を使用して、関心のある Python インタプリタ クラスごとに異なる Rust 構造を生成し、生成されたこれらの構造体を使用して Python プログラムのメモリ レイアウトを把握します。
アドレス空間レイアウトのランダム化のため、Python インタプリタのメモリ アドレスの取得は少し難しい場合があります。ターゲットの Python インタープリターにシンボルが同梱されている場合、Python のバージョンに応じてinterp_head
または_PyRuntime
変数を逆参照することで、インタープリターのメモリ アドレスを把握するのは非常に簡単です。ただし、Python の多くのバージョンは、ストリップされたバイナリとともに出荷されるか、Windows 上の対応する PDB シンボル ファイルなしで出荷されます。このような場合、BSS セクションをスキャンして、有効な PyInterpreterState を指していると思われるアドレスを探し、そのアドレスのレイアウトが期待どおりであるかどうかを確認します。
はい! py-spy は、x86_64 Linux および Windows 上で、C/C++ や Cython などの言語で書かれたネイティブ Python 拡張機能のプロファイリングをサポートします。コマンドラインで--native
渡すことで、このモードを有効にできます。最良の結果を得るには、シンボルを使用して Python 拡張機能をコンパイルする必要があります。 Cython プログラムに関しては、py-spy が元の .pyx ファイルの行番号を返すために、生成された C または C++ ファイルが必要であることにも注目してください。詳細については、ブログ投稿をお読みください。
--subprocesses
フラグをレコードまたはトップ ビューに渡すことにより、py-spy には、ターゲット プログラムの子プロセスである Python プロセスからの出力も含まれます。これは、マルチプロセッシングまたは Gunicorn ワーカー プールを使用するアプリケーションのプロファイリングに役立ちます。 py-spy は、作成される新しいプロセスを監視し、それらに自動的にアタッチし、出力にそれらのサンプルを含めます。レコード ビューには、コールスタック内の各プログラムの PID とコマンドラインが含まれ、サブプロセスは親プロセスの子として表示されます。
py-spy は、別の Python プロセスからメモリを読み取ることで動作しますが、OS やシステム設定によっては、セキュリティ上の理由からこれが許可されない場合があります。多くの場合、(sudo などを使用して) root ユーザーとして実行すると、これらのセキュリティ制限を回避できます。 OSX では常に root として実行する必要がありますが、Linux では、py-spy の起動方法とシステムのセキュリティ設定によって異なります。
Linux では、子プロセスではないプロセスに接続する場合、デフォルト設定では root 権限が必要になります。 py-spy の場合、これは、py-spy にプロセス ( py-spy record -- python myprogram.py
) を作成させることで root アクセスなしでプロファイリングできることを意味しますが、PID を指定して既存のプロセスにアタッチするには、通常 root ( sudo py-spy record --pid 123456
が必要になります。 sudo py-spy record --pid 123456
)。 Linux では、ptrace_scope sysctl 変数を設定することで、この制限を削除できます。
py-spy は、コードをアクティブに実行しているスレッドからのスタック トレースのみを含めようとし、スリープ状態またはアイドル状態のスレッドを除外しようとします。可能な場合、py-spy は、Linux では/proc/PID/stat
を読み取り、OSX では mach thread_basic_info 呼び出しを使用し、現在の SysCall がアイドル状態であることがわかっているかどうかを確認することによって、OS からこのスレッド アクティビティ情報を取得しようとします。 Windows 上で。
ただし、このアプローチにはいくつかの制限があり、アイドル状態のスレッドが依然としてアクティブとしてマークされる可能性があります。まず、プログラムを一時停止する前に、このスレッドのアクティビティ情報を取得する必要があります。一時停止したプログラムからこれを取得すると、常にアイドル状態であることが返されるためです。これは、スレッド アクティビティを取得した後、スタック トレースを取得したときにスレッドが異なる状態になる、潜在的な競合状態が存在することを意味します。 OS にスレッド アクティビティを問い合わせる機能も、Linux 上の FreeBSD および i686/ARM プロセッサにはまだ実装されていません。 Windows では、標準入力から入力を読み取るときなど、IO でブロックされた呼び出しもまだアイドルとしてマークされません。最後に、一部の Linux 呼び出しでは、使用している ptrace Attach によってアイドル状態のスレッドが一時的にウェイクアップし、procfs からの読み取り時に誤検知が発生する可能性があります。これらの理由から、Python の既知の特定の呼び出しをアイドル状態としてマークするヒューリスティック フォールバックも用意しています。
--idle
フラグを設定すると、この機能を無効にできます。これには、py-spy がアイドル状態と見なすフレームが含まれます。
GIL アクティビティを取得するには、Python 3.6 以前では_PyThreadState_Current
シンボルが指す threadid 値を調べ、Python 3.7 以降では_PyRuntime
構造体から同等のものを見つけ出します。これらのシンボルは Python ディストリビューションに含まれていない可能性があり、どのスレッドが GIL を保持しているかの解決が失敗する原因となります。現在の GIL 使用量もtop
ビューに %GIL として表示されます。
--gil
フラグを渡すと、グローバル インタプリタ ロックを保持しているスレッドのトレースのみが含まれます。場合によっては、これは Python プログラムがどのように時間を費やしているかをより正確に表示できる可能性がありますが、アクティブなまま GIL を解放する拡張機能のアクティビティが見逃されることに注意する必要があります。
OSX にはシステム整合性保護と呼ばれる機能があり、root ユーザーであっても /usr/bin にあるバイナリからメモリを読み取ることができなくなります。残念ながら、これには OSX に付属の Python インタープリタが含まれています。
これに対処するには、いくつかの方法があります。
docker コンテナ内で py-spy を実行すると、root として実行している場合でも、通常、アクセス許可拒否エラーが表示されます。
このエラーは、使用している process_vm_readv システム コールを docker が制限していることが原因で発生します。これは、Docker コンテナーの起動時に--cap-add SYS_PTRACE
を設定することでオーバーライドできます。
あるいは、docker-compose yaml ファイルを編集することもできます
your_service:
cap_add:
- SYS_PTRACE
この設定を有効にするには、Docker コンテナを再起動する必要があることに注意してください。
ホスト OS から py-spy を使用して、Docker コンテナ内で実行されている実行プロセスをプロファイリングすることもできます。
py-spy がプロセス メモリを読み取るにはSYS_PTRACE
が必要です。 Kubernetes はデフォルトでその機能を削除し、エラーが発生します
Permission Denied: Try running again with elevated permissions by going 'sudo env "PATH=$PATH" !!'
これに対処する推奨される方法は、仕様を編集してその機能を追加することです。デプロイメントの場合、これをDeployment.spec.template.spec.containers
に追加することで実行されます。
securityContext:
capabilities:
add:
- SYS_PTRACE
詳細については、こちらをご覧ください: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capabilities-for-a-container これにより、既存のポッドが削除され、再度作成されることに注意してください。 。
Alpine Python は、 manylinux
ホイールをオプトアウトします: pypa/pip#3969 (コメント)。次のようにすることで、この動作をオーバーライドして pip を使用して py-spy を Alpine にインストールできます。
echo 'manylinux1_compatible = True' > /usr/local/lib/python3.7/site-packages/_manylinux.py
あるいは、GitHub リリース ページから musl バイナリをダウンロードすることもできます。
--nonblocking
オプションを設定すると、py-spy はプロファイリング元のターゲット Python を一時停止しません。 py-spy を使用したプロセスからのサンプリングによるパフォーマンスへの影響は通常非常に低いですが、このオプションを設定すると、実行中の Python プログラムが中断されることが完全に回避されます。
このオプションを設定すると、py-spy は代わりに、実行中の Python プロセスからインタープリタの状態を読み取ります。メモリの読み取りに使用する呼び出しはアトミックではなく、スタック トレースを取得するために複数の呼び出しを発行する必要があるため、サンプリング時にエラーが発生する場合があります。これは、サンプリング時のエラー率の増加として、または出力に部分的なスタック フレームが含まれることとして現れることがあります。
まだです =)。
py-spy に欲しい機能がある場合は、適切な問題を高く評価するか、不足している機能を説明する新しい問題を作成してください。
py-spy は CLICOLOR 仕様に従っているため、環境でCLICOLOR_FORCE=1
を設定すると、ポケットベルにパイプされた場合でも、py-spy はカラー出力を印刷します。
py-spy は、rbspy に関する Julia Evans の優れた研究から多大な影響を受けています。特に、フレームグラフ ファイルとスピードスコープ ファイルを生成するコードは rbspy から直接取得されており、このプロジェクトでは rbspy から分離された read-process-memory および proc-maps クレートを使用します。
py-spy は MIT ライセンスに基づいてリリースされています。全文については LICENSE ファイルを参照してください。