web-dev-qa-db-ja.com

Linuxのperfユーティリティはスタックトレースをどのように理解しますか?

Linuxのperfユーティリティは、c/c ++、jvmコード、nodejsコードなどのフレームグラフを生成するためにBrendanGreggによって有名に使用されています。

Linuxカーネルはスタックトレースをネイティブに理解していますか?プロセスが完全に異なる言語で記述されている場合でも、ツールがプロセスのスタックトレースをどのようにイントロスペクトできるかについての詳細はどこで読むことができますか?

14
Shahbaz

グレッグによるperfのスタックトレースについての短い紹介があります: http://www.brendangregg.com/perf.html

4.4スタックトレース

常にフレームポインタを使用してコンパイルします。フレームポインタを省略することは、デバッガを壊す悪のコンパイラ最適化であり、悲しいことに、多くの場合、デフォルトです。それらがないと、perf_eventsから不完全なスタックが表示される場合があります...これを修正するには、ドワーフデータを使用してスタックを巻き戻すか、フレームポインタを返すかの2つの方法があります。

ドワーフ

約3.9カーネル以降、perf_eventsは、ユーザーレベルのスタックで欠落しているフレームポインターの回避策をサポートしています。libunwindはdwarfを使用します。これは、「-gdwarf」を使用して有効にできます。 ...コンパイラの最適化(-O2)。この場合はフレームポインタが省略されています。 ...再コンパイル..-fno-omit-frame-pointerを使用:

Cスタイル以外の言語では、フレーム形式が異なる場合や、フレームポインタが省略されている場合があります。

4.3。 JITシンボル(Java、Node.js)

JavaのJVMやノードのv8などの仮想マシン(VM)を持つプログラムは、関数を実行してスタックを管理する独自の方法を持つ独自の仮想プロセッサを実行します。 perf_eventsを使用してこれらをプロファイリングすると、VMエンジン..perf_eventsにはこれを解決するためのJITサポートがあり、これを維持するにはVMシンボル変換用の/tmp/perf-PID.mapファイル。

Javaは、x86のホットスポットがフレームポインタを省略しているため(gccと同様)、最初から完全なスタックを表示しない場合があることに注意してください。新しいバージョン(JDK 8u60 +)では、-XX:+PreserveFramePointerオプションを使用できます。この動作を修正するには、.。

Javaおよびスタックトレースに関するGreggのブログ投稿: http://techblog.netflix.com/2015/07/Java-in-flames.html ( "修正フレームポインタ」-一部のJDK8バージョンおよびJDK9で、プログラムの起動時にオプションを追加することで修正されました)

さて、あなたの質問:

Linuxのperfユーティリティはスタックトレースをどのように理解しますか?

perfutility基本的に(初期バージョンでは)Linuxカーネルのサブシステム「perf_events」(または「events ")、syscallでアクセス perf_event_open 。コールスタックトレースには、オプションPERF_SAMPLE_CALLCHAIN/PERF_SAMPLE_STACK_USERがあります:

sample_type PERF_SAMPLE_CALLCHAINコールチェーンを記録します(スタックバックトレース)。

          PERF_SAMPLE_STACK_USER (since Linux 3.7)
                 Records the user level stack, allowing stack unwinding.

Linuxカーネルはスタックトレースをネイティブに理解していますか?

CPUアーキテクチャによっては、理解できる場合と理解できない場合があります(実装されている場合)。サンプリング(ライブプロセスからのコールスタックの取得/読み取り)コールチェーンの機能は、カーネルのアーキテクチャに依存しない部分で、本体が空の__weakとして定義されています。

http://lxr.free-electrons.com/source/kernel/events/callchain.c?v=4.4#L26

 27 __weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
 28                                   struct pt_regs *regs)
 29 {
 30 }
 31 
 32 __weak void perf_callchain_user(struct perf_callchain_entry *entry,
 33                                 struct pt_regs *regs)
 34 {
 35 }

4.4カーネルでは、x86/x86_64、ARC、SPARC、ARM/ARM64、Xtensa、Tilera TILE、PowerPC、Imagination Metaのカーネルのアーキテクチャ依存部分で、ユーザースペースコールチェーンサンプラーが再定義されています。

http://lxr.free-electrons.com/ident?v=4.4;i=perf_callchain_user

Arch/x86/kernel/cpu/perf_event.c, line 2279
Arch/arc/kernel/perf_event.c, line 72
Arch/sparc/kernel/perf_event.c, line 1829
Arch/arm/kernel/perf_callchain.c, line 62
Arch/xtensa/kernel/perf_event.c, line 339
Arch/tile/kernel/perf_event.c, line 995
Arch/arm64/kernel/perf_callchain.c, line 109
Arch/powerpc/perf/callchain.c, line 490
Arch/metag/kernel/perf_callchain.c, line 59

一部のアーキテクチャやモードでは、ユーザースタックからのコールチェーンの読み取りは簡単ではない場合があります。

どのCPUアーキテクチャを使用していますか?どの言語とVMが使用されていますか?

プロセスが完全に異なる言語で記述されている場合でも、ツールがプロセスのスタックトレースをどのようにイントロスペクトできるかについての詳細はどこで読むことができますか?

言語のgdbおよび/またはデバッガー、またはlibcの backtrace function 、またはlibunwindでの読み取り専用アンワインドのサポートを試すことができます( ローカルがあります) libunwindのバックトレースの例show_backtrace())。

それらは、フレーム解析のより良いサポート/言語の仮想マシンまたはアンワインド情報とのより良い統合を持っているかもしれません。 gdb(backtraceコマンドを使用)または他のデバッガーが実行中のプログラムからスタックトレースを取得できない場合、スタックトレースを取得する方法がまったくない可能性があります。

呼び出しトレースを取得できるが、perfが取得できない場合(C/C++の場合は-fno-omit-frame-pointerで再コンパイルした後でも)、アーキテクチャとフレーム形式のこのような組み合わせのサポートをperf_eventsperf

一般的なバックトレースの問題と解決策に関する情報が記載されたブログがいくつかあります。

perf_events/perfのドワーフサポート:

27
osgx