第一人者モードで最初のstapスクリプトを書いたので、私はもうカーネルモジュールを書いたことはありません。stapはとても便利なので、スクリプト言語としてC言語で再生できます。
私が書くstapスクリプトがますます複雑になるにつれて、コードの見栄えを良くするために、いくつかの半分ぶら下がっているデザインパターンを使用する必要があります。たとえば、stapスクリプトですべてを書くことはもうしません。いくつかの一般的な構造を定義します。 、マクロ定義はヘッダーファイルに書き込まれます。
// common.h
#define VAR 100
次に、それらをstapスクリプトで引用します。
#!/usr/local/bin/stap -g
// aa.stp
#!/usr/local/bin/stap -g
%{
#include "common.h"
%}
function func(who:long)
%{
STAP_PRINTF("%d %lu\n", VAR, STAP_ARG_who);
%}
probe begin
{
func($1);
exit();
}
しかし、それは機能しません。エラーを報告します:
[root@localhost test]# ./aa.stp 1
/tmp/stapxVC8YT/stap_c5a511d01be2d078445a0de2c20c8a7f_1308_src.c:29:20: 致命错误:common.h:没有那个文件或目录
#include "common.h"
^
编译中断。
make[1]: *** [/tmp/stapxVC8YT/stap_c5a511d01be2d078445a0de2c20c8a7f_1308_src.o] 错误 1
make: *** [_module_/tmp/stapxVC8YT] 错误 2
WARNING: kbuild exited with status: 2
Pass 4: compilation failed. [man error::pass4]
明らかにcommon.hは現在のディレクトリにありますが、見つかりません。この問題を解決する方法は?
実際、stapの原理を理解している人なら誰でも、何が起こっているのかを理解できます。-vvvを使用するだけで、詳細なログをエクスポートし、makeパラメーターを確認できます。
[root@localhost test]# ./aa.stp 1 -vvv >./log 2>&1
[root@localhost test]# grep -rn 'make -C' ./log
521:Running env -uARCH -uKBUILD_EXTMOD -uCROSS_COMPILE -uKBUILD_IMAGE -uKCONFIG_CONFIG -uINSTALL_PATH -uLD_LIBRARY_PATH PATH=/usr/bin:/bin:/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/bin:/sbin make -C /lib/modules/3.10.0-327.x86_64/build M=/tmp/stapmjgFbY modules CONFIG_DEBUG_INFO= CONFIG_STACK_VALIDATION= CONFIG_MODVERSIONS= ARCH=x86_64 V=1 -j2
ご覧のとおり、インクルードディレクトリには現在のディレクトリがまったく含まれていません。
現在のディレクトリを含める場合は、Makefileに次のオプションを追加する必要があります。
EXTRA_CFLAGS += -I$(pwd)
しかし、それをどのように行うのですか?
さて、stapソースコードを変更するのが最も簡単です。Makefileを生成するロジックを直接見つけることができ、上記の文字列をMakefileの最後に埋め込みます。職人技を終えた後、私は意図的にこの再コンパイル方法を試しました。これは本当に簡単です。
grep EXTRA_CFLAGSは、buildrun.cxxファイルのcompile_pass関数を見つけて、以下を追加します。
string module_cflags = "EXTRA_CFLAGS";
o << module_cflags << " :=" << endl;
// 以下为新增行
o << module_cflags << "EXTRA_CFLAGS += -I$(pwd)" << endl;
次に、再コンパイルします。
しかし、これは職人がすることではありません!職人は、問題を完了するためにコードを変更したり、再コンパイルしたりする必要はありません。
テールスネークを噛んで遊びすぎた後は、シンプルなアイデアです。自分でタップしてみましょう。
stapを使用して、実行中のstapのMakefileコマンド生成関数をフックし、コマンドが生成される前にMakefileの文字列をパディングします。
ソースコードに依存せずにフックする必要のある関数を見つけるために、私たちは推測にのみ頼ることができます:
stap -L 'process("/usr/local/bin/stap").function("*make*")'
上記のコマンドはリストをエクスポートします。作成者の関数の名前の明確さに応じて、1時間1つずつ推測しようとし、最終的に次の関数を決定しました。
process("/usr/local/bin/stap").function("make_any_make_cmd@/usr/test/systemtap/buildrun.cxx:96") $s:struct systemtap_session& $dir:string const& $target:string const& $newpath:string $make_cmd:class vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >
stap-Lの代わりに文字列/ usr / local / bin / stap | grepmakeを使い始めました。
ターゲット関数を見つけました。printfを介して、dirパラメーターがMakefileのディレクトリであることもわかりました。始めましょう。
#!/usr/local/bin/stap -g
function padding(dir:string)
%{
char mf[64];
// 增加一个新的环境变量,以引用任意地方的头文件
char *ext = "EXTRA_CFLAGS += -I$(STAP_INCLUDE)\n";
struct file *fp;
mm_segment_t old_fs;
loff_t pos;
snprintf(mf, 64, "%s/Makefile", (char *)STAP_ARG_dir);
// 在内核空间将EXTRA_CFLAGS追加到Makefile的最后
fp = filp_open(mf, O_RDWR | O_APPEND, 0644);
old_fs = get_fs();
set_fs(KERNEL_DS);
pos = fp->f_pos;
vfs_write(fp, ext, strlen(ext), &pos);
fp->f_pos = pos;
set_fs(old_fs);
filp_close(fp, NULL);
%}
probe process("/usr/local/bin/stap").function("make_any_make_cmd")
{
// 不懂C++,我是通过printf("%s\n", $dir$);找到_M_local_buf成员变量的。
padding(user_string($dir->_M_local_buf));
}
来て、効果を見てください:
[root@localhost test]# export STAP_INCLUDE=`pwd`
[root@localhost test]# echo $STAP_INCLUDE
/usr/test
[root@localhost test]# ./selftap -c '/usr/local/bin/stap -g ./aa.stp 3'
100 3
そうです。
aa.stpスクリプトが変更されず、その操作パラメーターも変更されない限り、後でaa.stpを実行するときに、セルフタップなしで直接実行できることに注意してください。
[root@localhost test]# ./aa.stp 3
100 3
ただし、スクリプト自体またはパラメータが変更されたら、もう一度セルフタップする必要があります。
[root@localhost test]# ./aa.stp 3
100 3
[root@localhost test]# ./aa.stp 5
/tmp/stapLTrZIV/stap_4e39e7642313b2bf2a6d9120e03487ab_1308_src.c:29:20: 致命错误:common.h:没有那个文件或目录
#include "common.h"
^
编译中断。
make[1]: *** [/tmp/stapLTrZIV/stap_4e39e7642313b2bf2a6d9120e03487ab_1308_src.o] 错误 1
make: *** [_module_/tmp/stapLTrZIV] 错误 2
WARNING: kbuild exited with status: 2
Pass 4: compilation failed. [man error::pass4]
[root@localhost test]# ./selftap -c '/usr/local/bin/stap -g ./aa.stp 5'
100 5
[root@localhost test]# ./aa.stp 5
100 5
これは、動作中のstapキャッシュメカニズムです。最後の操作のすべてが〜/ .systemtap / cacheディレクトリにキャッシュされます。
実際、この記事に示されている職人技は感情を表現することを目的としています。ソフトウェアの動作原理を理解していれば、ソースコードを変更せずに、任意に投げて動作を変更できます。
stapが実際にカーネルモジュールにコンパイルして作業を完了するための主要な前提条件であることを理解した後、コンパイルコマンドを生成するプロセスをフックでき、Makefileには特別な目的を達成するための任意のコマンドがあります。
浙江文州の革靴は濡れているので、雨でも太りません。