5

長い投稿で申し訳ありません。短い方法で作成するのに苦労しています。ftraceまた、これは Unix & Linux Stack Exchange に適しているかもしれませんが、タグがあるので、まずここで試してみます。

function_graphとにかく - ユーザー プログラムの機械語命令がを使用したフル キャプチャのコンテキストで実行されることを確認したいと思いますftrace。1 つの問題は、古いカーネルにこれが必要なことです。

$ uname -a
Linux mypc 2.6.38-16-generic #67-Ubuntu SMP Thu Sep 6 18:00:43 UTC 2012 i686 i686 i386 GNU/Linux

...そして、この版ではUPROBESUprobes in 3.5 [LWN.net]が指摘しているように、そのようなことができるはずです。(元のカーネルにパッチを当てる必要がない限り、User-Space Probes (Uprobes) [chunghwan.com]が示し​​ているように、ツリーから構築されたカーネル モジュールを試しても構わないと思っています。私は0から見ることができます: Inodeベースのuprobes [LWN.net]、2.6にはおそらく完全なパッチが必要です)

ただし、このバージョンでは/sys/kernel/debug/kprobes、および/sys/kernel/debug/tracing/kprobe_events;があります。Documentation/trace/kprobetrace.txtは、kprobe をアドレスに直接設定できることを意味します。これがどのように使用されているかの例がどこにも見つからなくても。

いずれにせよ、どのアドレスを使用すればよいかはまだわかりません。小さな例として、プログラムのmain関数の開始をトレースしたいとしましょう(以下に含まれています)。wtest.cこれを実行して、機械語命令のアセンブリ リストをコンパイルして取得できます。

$ gcc -g -O0 wtest.c -o wtest
$ objdump -S wtest | less
...
08048474 <main>:
int main(void) {
 8048474:       55                      push   %ebp
 8048475:       89 e5                   mov    %esp,%ebp
 8048477:       83 e4 f0                and    $0xfffffff0,%esp
 804847a:       83 ec 30                sub    $0x30,%esp
 804847d:       65 a1 14 00 00 00       mov    %gs:0x14,%eax
 8048483:       89 44 24 2c             mov    %eax,0x2c(%esp)
 8048487:       31 c0                   xor    %eax,%eax
  char filename[] = "/tmp/wtest.txt";
...
  return 0;
 804850a:       b8 00 00 00 00          mov    $0x0,%eax
}
...

このスクリプトを使用して ftrace ロギングを設定します。

sudo bash -c '
KDBGPATH="/sys/kernel/debug/tracing"
echo function_graph > $KDBGPATH/current_tracer
echo funcgraph-abstime > $KDBGPATH/trace_options
echo funcgraph-proc > $KDBGPATH/trace_options
echo 0 > $KDBGPATH/tracing_on
echo > $KDBGPATH/trace
echo 1 > $KDBGPATH/tracing_on ; ./wtest ; echo 0 > $KDBGPATH/tracing_on
cat $KDBGPATH/trace > wtest.ftrace
'

デバッグで (そうでなければ複雑な) 結果のftraceログ の一部を見ることができます- カーネル空間でのハードディスク書き込みの観察 (ドライバー/モジュールを使用) - Unix & Linux Stack Exchange (例を入手した場所)。

基本的に、0x8048474、0x8048475、0x8048477、0x804847a、0x804847d、0x8048483、および 0x8048487ftraceの最初の命令が (任意の) CPU によって実行されたときに、このログに出力したいと思います。問題は、 Anatomy of a Program in Memory : Gustavo Duartemainから理解できる限り、これらのアドレスは、プロセス自体の観点から見た仮想アドレスです (そして、私が収集すると、同じ観点が によって示されます)。 ..どうやら、物理的な住所が必要なのでしょうか?/proc/PID/mapskrpobe_event

したがって、私の考えは次のようになります。プログラムの逆アセンブリの仮想アドレスに対応する物理アドレスを見つけることができれば (たとえば、pid とアドレスを受け入れ、procfs を介して物理アドレスを返すカーネル モジュールをコーディングすることによって)、設定できます。上記のスクリプトを介して一種の「トレースポイント」としてアドレスを/sys/kernel/debug/tracing/kprobe_events取得し、うまくいけばftraceログに記録されます。原則として、これは機能しますか?

これに関する1つの問題は、Linux(ubuntu)、C言語で見つかりました:仮想アドレスから物理アドレスへの変換 - スタックオーバーフロー

ユーザー コードでは、仮想アドレスに対応する物理アドレスを知ることはできません。これは、単にカーネルの外部にエクスポートされない情報です。特にカーネルがプロセスのメモリの一部をスワップアウトすることを決定した場合は、いつでも変更される可能性があります。
...
systemcall/procfs を使用して仮想アドレスをカーネルに渡し、vmalloc_to_pfn を使用します。procfs/registers を介して物理アドレスを返します。

ただし、vmalloc_to_pfn些細なことでもないようです。

x86 64 - vmalloc_to_pfn は、Linux 32 システムで 32 ビット アドレスを返します。PAE 物理アドレスの上位ビットが切り落とされるのはなぜですか? - スタックオーバーフロー

VA: vmalloc_to_pfn: 0x36f7f7fc を使用する 0xf8ab87fc PA。しかし、私は実際に期待しています: 0x136f7f7fc.
...
物理アドレスは 4 ~ 5 GB です。しかし、正確な物理アドレスを取得することはできません。切り捨てられた 32 ビット アドレスしか取得できません。真の物理アドレスを取得する別の方法はありますか?

したがって、kprobes によって追跡される物理アドレスをどの程度確実に抽出できるかはわかりません。特に「いつでも変更される可能性がある」ためです。しかし、ここでは、プログラムが小さくて些細なことなので、トレース中にプログラムがスワップされない合理的な可能性があり、適切なキャプチャが得られることを願っています。(したがって、上記のデバッグ スクリプトを複数回実行する必要がある場合でも、10 回に 1 回 (または 100 回でも) 「適切な」キャプチャを取得できることを期待できる限り、問題ありません。 )

ftraceタイムスタンプが同じドメインで表現されるように、を介して出力が必要であることに注意してください (信頼できる Linux カーネルのタイムスタンプ (またはその調整) と両方の usbmon と ftrace? -タイムスタンプに関する問題の図については、スタック オーバーフローを参照してください)。したがって、たとえば、ユーザー空間からプログラムを実行およびトレースするためのスクリプトを考え出すことができたとしてもgdb(同時にキャプチャが取得されます)、それ自体からのオーバーヘッドがログに表示さftraceれるため、それを避けたいと思います。.gdbftrace

つまり、要約すると:

  • 仮想(実行可能ファイルの逆アセンブリから)アドレスから(おそらく別のカーネルモジュールを介して)物理アドレスを取得するアプローチはありますか?もしそうなら、このアドレス変換の目的で使用できるカーネルモジュールの例はありますか?
  • 特定のメモリアドレスが実行されているときに、カーネルモジュールを使用してコールバック/ハンドラ関数を「登録」できますか? 次にtrace_printk、その関数で a を使用してftraceログを取得するだけで (または、それがなくても、ハンドラー関数名自体がftraceログに表示されるはずです)、それでオーバーヘッドが多すぎるとは思われません...

実際、この 2007 年の投稿、Jim Keniston - utrace ベースの uprobes: systemtap メーリング リストには、11. Uprobes Example(に追加されたDocumentation/uprobes.txt) があります。これは、まさにそのように思われます - ハンドラー関数を登録するカーネル モジュールです。残念ながら、それはlinux/uprobes.h;を使用します。kprobes.hそして、私は私の中にしかありません/usr/src/linux-headers-2.6.38-16/include/linux/。また、私のシステムでは、有効になっていないことsystemtapについて不平を言うことさえあります(このコメントを参照)...したがって、カーネルを再コンパイルしてuprobesを取得することなく、必要なデバッグトレースを取得するために使用できる他のアプローチがある場合、それは知ってよかった...CONFIG_UTRACE


wtest.c:

#include <stdio.h>
#include <fcntl.h>  // O_CREAT, O_WRONLY, S_IRUSR

int main(void) {
  char filename[] = "/tmp/wtest.txt";
  char buffer[] = "abcd";
  int fd;
  mode_t perms = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;

  fd = open(filename, O_RDWR|O_CREAT, perms);
  write(fd,buffer,4);
  close(fd);

  return 0;
}
4

1 に答える 1