sda*_*aau 5 linux debugging ftrace linux-kernel
对于冗长的帖子道歉,我在以较短的方式制定它时遇到了麻烦.此外,这可能更适合Unix和Linux Stack Exchange,但我会先在SO处尝试,因为有一个ftrace
标签.
无论如何 - 我想观察用户程序的机器指令是否在完全function_graph
捕获的上下文中执行ftrace
.一个问题是我需要这个旧内核:
$ 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
Run Code Online (Sandbox Code Playgroud)
......在这个版本中,没有UPROBES
- 正如3.5 [LWN.net]中的Uprobes应该能够做到这一点.(只要我不需要修补原始内核,我就会愿意尝试使用树构建的内核模块,因为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
}
...
Run Code Online (Sandbox Code Playgroud)
我会通过这个脚本设置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
'
Run Code Online (Sandbox Code Playgroud)
您可以ftrace
在调试中看到(否则是复杂的)结果日志的一部分 - 观察内核空间中的硬盘写入(带有驱动程序/模块) - Unix和Linux Stack Exchange(我从中得到了示例).
基本上,我想在这个ftrace
日志中打印输出,当第一条指令main
- 例如0x8048474,0x8048475,0x8048477,0x804847a,0x804847d,0x8048483和0x8048487的指令 - 由(任何)CPU执行时.问题是,据我从内存程序剖析中可以理解:Gustavo Duarte,这些地址是虚拟地址,从过程本身的角度来看(我收集的是,相同的视角显示/proc/PID/maps
). ..显然,因为krpobe_event
我需要一个实际地址?
所以,我的想法是:如果我能找到对应于程序反汇编的虚拟地址的物理地址(比如编写一个内核模块,它可以接受pid和地址,并通过procfs返回物理地址),我可以设置通过/sys/kernel/debug/tracing/kprobe_events
上面的脚本将地址作为一种"跟踪点" - 并希望将它们放入ftrace
日志中.原则上这可行吗?
有一个问题,我发现在Linux(ubuntu)上,C语言:虚拟到物理地址转换 - Stack Overflow:
在用户代码中,您无法知道与虚拟地址对应的物理地址.这是信息根本不在内核之外导出.它甚至可以随时更改,尤其是在内核决定交换部分进程内存的情况下.
...
使用systemcall/procfs将虚拟地址传递给内核并使用vmalloc_to_pfn.通过procfs/registers返回物理地址.
但是,vmalloc_to_pfn
似乎也不是微不足道的:
x86 64 - vmalloc_to_pfn在Linux 32系统上返回32位地址.为什么它会切断PAE物理地址的高位?- 堆栈溢出
VA:0xf8ab87fc PA使用vmalloc_to_pfn:0x36f7f7fc.但我实际上期待:0x136f7f7fc.
...
物理地址介于4到5 GB之间.但我无法得到确切的物理地址,我只能得到切断的32位地址.有另一种获得真实物理地址的方法吗?
所以,我不确定我是如何可靠地提取物理地址所以它们被kprobes追踪 - 特别是因为"它甚至可以在任何时候改变".但是在这里,我希望由于程序很小而且微不足道,程序在跟踪时不会交换,从而可以获得适当的捕获.(所以即使我必须多次运行调试脚本,只要我希望在10次(甚至100次)中获得"正确"捕获,我就可以了.)
请注意,我希望输出通过ftrace
,以便时间戳在同一个域中表示(请参阅可靠的Linux内核时间戳(或其调整)以及usbmon和ftrace? - Stack Overflow用于说明时间戳问题).因此,即使我能想出一个gdb
脚本,从用户空间运行和跟踪程序(同时ftrace
获取捕获) - 我想避免这种情况,因为gdb
它本身的开销将显示在ftrace
日志中.
所以,总结一下:
trace_printk
在该函数中使用一个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
抱怨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;
}
Run Code Online (Sandbox Code Playgroud)
显然,使用 3.5+ 内核上的内置 uprobes 会容易得多;但考虑到我的内核 2.6.38 的 uprobes 是一个非常深入的补丁(我无法真正将其隔离在单独的内核模块中,以避免修补内核),以下是我可以注意到的独立补丁2.6.38 上的模块。(由于我仍然不确定很多事情,我仍然希望看到一个答案来纠正这篇文章中的任何误解。)
\n\n我想我已经到达了某个地方,但不是kprobes
。我不确定,但似乎我设法获得了正确的物理地址;然而,kprobes
文档明确指出,当使用“ @ADDR:在ADDR处获取内存(ADDR应该在内核中) ”时;我得到的物理地址低于 0xc0000000 的内核边界(但是,0xc0000000 通常与虚拟内存布局一起?)。
所以我使用了一个硬件断点 - 该模块在下面,但是买者自负 - 它的行为是随机的,有时会导致内核哎呀!。通过编译模块并运行bash
:
$ sudo bash -c \'KDBGPATH="/sys/kernel/debug/tracing" ;\necho function_graph > $KDBGPATH/current_tracer ; echo funcgraph-abstime > $KDBGPATH/trace_options\necho funcgraph-proc > $KDBGPATH/trace_options ; echo 8192 > $KDBGPATH/buffer_size_kb ;\necho 0 > $KDBGPATH/tracing_on ; echo > $KDBGPATH/trace\'\n$ sudo insmod ./callmodule.ko && sleep 0.1 && sudo rmmod callmodule && \\\ntail -n25 /var/log/syslog | tee log.txt && \\\nsudo cat /sys/kernel/debug/tracing/trace >> log.txt\n
Run Code Online (Sandbox Code Playgroud)\n\n...我得到一个日志。我想追踪 of 的前两条指令main()
,wtest
对我来说是:
$ objdump -S wtest/wtest | grep -A3 \'int main\'\nint main(void) {\n 8048474: 55 push %ebp\n 8048475: 89 e5 mov %esp,%ebp\n 8048477: 83 e4 f0 and $0xfffffff0,%esp\n
Run Code Online (Sandbox Code Playgroud)\n\n...在虚拟地址 0x08048474 和 0x08048475 处。在里面syslog
输出中,我可以得到:
...\n[ 1106.383011] callmodule: parent task a: f40a9940 c: kworker/u:1 p: [14] s: stopped\n[ 1106.383017] callmodule: - wtest [9404]\n[ 1106.383023] callmodule: Trying to walk page table; addr task 0xEAE90CA0 ->mm ->start_code: 0x08048000 ->end_code: 0x080485F4\n[ 1106.383029] callmodule: walk_ 0x8048000 callmodule: Valid pgd : Valid pud: Valid pmd: page frame struct is @ f63e5d80; *virtual (page_address) @ (null) (is_vmalloc_addr 0 virt_addr_valid 0 virt_to_phys 0x40000000) page_to_pfn 639ec page_to_phys 0x639ec000\n[ 1106.383049] callmodule: walk_ 0x80483c0 callmodule: Valid pgd : Valid pud: Valid pmd: page frame struct is @ f63e5d80; *virtual (page_address) @ (null) (is_vmalloc_addr 0 virt_addr_valid 0 virt_to_phys 0x40000000) page_to_pfn 639ec page_to_phys 0x639ec000\n[ 1106.383067] callmodule: walk_ 0x8048474 callmodule: Valid pgd : Valid pud: Valid pmd: page frame struct is @ f63e5d80; *virtual (page_address) @ (null) (is_vmalloc_addr 0 virt_addr_valid 0 virt_to_phys 0x40000000) page_to_pfn 639ec page_to_phys 0x639ec000\n[ 1106.383083] callmodule: physaddr : (0x080483c0 ->) 0x639ec3c0 : (0x08048474 ->) 0x639ec474\n[ 1106.383106] callmodule: 0x08048474 id [3]\n[ 1106.383113] callmodule: 0x08048475 id [4]\n[ 1106.383118] callmodule: (( 0x08048000 is_vmalloc_addr 0 virt_addr_valid 0 ))\n[ 1106.383130] callmodule: cont pid task a: eae90ca0 c: wtest p: [9404] s: runnable\n[ 1106.383147] initcall callmodule_init+0x0/0x1000 [callmodule] returned with preemption imbalance\n[ 1106.518074] callmodule: < exit\n
Run Code Online (Sandbox Code Playgroud)\n\n...这意味着它将虚拟地址 0x08048474 映射到物理地址 0x639ec474。但是,物理断点不用于硬件断点 - 我们可以直接提供虚拟地址register_user_hw_breakpoint
;但是,我们还需要提供该task_struct
过程的信息。这样,我就可以在ftrace
输出中得到类似这样的内容:
...\n 597.907256 | 1) wtest-5339 | | handle_mm_fault() {\n...\n 597.907310 | 1) wtest-5339 | + 35.627 us | }\n 597.907311 | 1) wtest-5339 | + 46.245 us | }\n 597.907312 | 1) wtest-5339 | + 56.143 us | }\n 597.907313 | 1) wtest-5339 | 1.039 us | up_read();\n 597.907317 | 1) wtest-5339 | 1.285 us | native_get_debugreg();\n 597.907319 | 1) wtest-5339 | 1.075 us | native_set_debugreg();\n 597.907322 | 1) wtest-5339 | 1.129 us | native_get_debugreg();\n 597.907324 | 1) wtest-5339 | 1.189 us | native_set_debugreg();\n 597.907329 | 1) wtest-5339 | | () {\n 597.907333 | 1) wtest-5339 | | /* callmodule: hwbp hit: id [3] */\n 597.907334 | 1) wtest-5339 | 5.567 us | }\n 597.907336 | 1) wtest-5339 | 1.123 us | native_set_debugreg();\n 597.907339 | 1) wtest-5339 | 1.130 us | native_get_debugreg();\n 597.907341 | 1) wtest-5339 | 1.075 us | native_set_debugreg();\n 597.907343 | 1) wtest-5339 | 1.075 us | native_get_debugreg();\n 597.907345 | 1) wtest-5339 | 1.081 us | native_set_debugreg();\n 597.907348 | 1) wtest-5339 | | () {\n 597.907350 | 1) wtest-5339 | | /* callmodule: hwbp hit: id [4] */\n 597.907351 | 1) wtest-5339 | 3.033 us | }\n 597.907352 | 1) wtest-5339 | 1.105 us | native_set_debugreg();\n 597.907358 | 1) wtest-5339 | 1.315 us | down_read_trylock();\n 597.907360 | 1) wtest-5339 | 1.123 us | _cond_resched();\n 597.907362 | 1) wtest-5339 | 1.027 us | find_vma();\n 597.907364 | 1) wtest-5339 | | handle_mm_fault() {\n...\n
Run Code Online (Sandbox Code Playgroud)\n\n...其中与程序集对应的跟踪由断点 id 标记。值得庆幸的是,正如预期的那样,他们接踵而至。然而,ftrace
还捕获了一些中间的调试命令。无论如何,这就是我想看到的。
以下是有关该模块的一些注释:
\n\nkallsyms
,那么我使用指向该地址的函数指针;否则其他需要的功能是从源复制的SIGSTOP
(就其本身而言,此时似乎有点不可靠),并将状态设置为__TASK_STOPPED
)。\n\nwtest
它自然终止后很长时间内仍挂在进程列表中,所以我猜想作品。page_to_phys()
以获取该地址(内部通过页帧号);LDD3 ch.15 有助于理解 pfn 和物理地址之间的关系。\n\nobjdump
汇编输出计算偏移量 - 不过,我不能 100% 确定这是正确的。0x08048000
既不是is_vmalloc_addr
也不是virt_addr_valid
;我想,这应该告诉我,人们不可能既没有使用vmalloc_to_pfn()
也没有virt_to_page()
到达它的物理地址!?kprobes
从内核空间进行设置ftrace
有点棘手(需要复制函数)\n\nkprobe
,总是导致“无法插入探针(-22) ”kallsyms
的函数地址tracing_on()
(0xc10bcf60);这似乎有效 - 因为它引发了一个致命的“ BUG:原子调度”(显然,我们不打算在 module_init 中设置断点?)。Bug 是致命的,因为它使kprobes
目录从ftrace
目录从调试目录kprobe
不会使其出现在ftrace
日志中 - 它还需要启用;启用所需的代码就在那里 - 但由于之前的错误,我从未尝试过HW_BREAKPOINT_X
,attr.bp_len
需要设置为sizeof(long)
printk
变量attr
- 某些东西会变得严重混乱,无论我接下来尝试打印什么变量,我都会得到它的值 0x5 (或 0x48)(?!)bp->id
就随机性而言,我认为这是因为该过程不是在停止状态下启动的;当它停止时,它最终会处于不同的状态(或者,很可能,我在某处缺少一些锁定)。无论如何,您还可以期待syslog
:
[ 1661.815114] callmodule: Trying to walk page table; addr task 0xEAF68CA0 ->mm ->start_code: 0x08048000 ->end_code: 0x080485F4\n[ 1661.815319] callmodule: walk_ 0x8048000 callmodule: Valid pgd : Valid pud: Valid pmd: page frame struct is @ f5772000; *virtual (page_address) @ c0000000 (is_vmalloc_addr 0 virt_addr_valid 1 virt_to_phys 0x0) page_to_pfn 0 page_to_phys 0x0\n[ 1661.815837] callmodule: walk_ 0x80483c0 callmodule: Valid pgd : Valid pud: Valid pmd: page frame struct is @ f5772000; *virtual (page_address) @ c0000000 (is_vmalloc_addr 0 virt_addr_valid 1 virt_to_phys 0x0) page_to_pfn 0 page_to_phys 0x0\n[ 1661.816846] callmodule: walk_ 0x8048474 callmodule: Valid pgd : Valid pud: Valid pmd: page frame struct is @ f5772000; *virtual (page_address) @ c0000000 (is_vmalloc_addr 0 virt_addr_valid 1 virt_to_phys 0x0) page_to_pfn 0 page_to_phys 0x0\n
Run Code Online (Sandbox Code Playgroud)\n\n...也就是说,即使有正确的任务指针(通过start_code判断),也只能获得0x0作为物理地址。有时你会得到相同的结果,但是使用start_code: 0x00000000 ->end_code: 0x00000000
. 有时,task_struct
即使 pid 可以,也无法获取 a:
[ 833.380417] callmodule:c: pid 7663\n[ 833.380424] callmodule: everything all right; pid 7663 (7663)\n[ 833.380430] callmodule: p is NULL - exiting\n[ 833.516160] callmodule: < exit\n
Run Code Online (Sandbox Code Playgroud)\n\n好吧,希望有人能评论并澄清这个模块的一些行为:)
\n希望这对某人有帮助,
\n干杯!
Makefile
:
EXTRA_CFLAGS=-g -O0\nobj-m += callmodule.o\nall:\n make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules\nclean:\n make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean\n
Run Code Online (Sandbox Code Playgroud)\n\ncallmodule.c
:
#include <linux/module.h>\n#include <linux/slab.h> //kzalloc\n#include <linux/syscalls.h> // SIGCHLD, ... sys_wait4, ...\n#include <linux/kallsyms.h> // kallsyms_lookup, print_symbol\n#include <linux/highmem.h> // \xe2\x80\x98kmap_atomic\xe2\x80\x99 (via pte_offset_map)\n#include <asm/io.h> // page_to_phys (arch/x86/include/asm/io.h)\n\nstruct subprocess_infoB; // forward declare\n// global variable - to avoid intervening too much in the return of call_usermodehelperB:\nstatic int callmodule_pid;\nstatic struct subprocess_infoB* callmodule_infoB;\n#define TRY_USE_KPROBES 0 // 1 // enable/disable kprobes usage code\n#include <linux/kprobes.h> // enable_kprobe\n// for hardware breakpoint:\n#include <linux/perf_event.h>\n#include <linux/hw_breakpoint.h>\n\n// define a modified struct (with extra fields) here:\nstruct subprocess_infoB {\n struct work_struct work;\n struct completion *complete;\n char *path;\n char **argv;\n char **envp;\n int wait; //enum umh_wait wait;\n int retval;\n int (*init)(struct subprocess_info *info);\n void (*cleanup)(struct subprocess_info *info);\n void *data;\n pid_t pid;\n struct task_struct *task;\n unsigned long long last_page_physaddr;\n};\n\nstruct subprocess_infoB *call_usermodehelper_setupB(char *path, char **argv,\n char **envp, gfp_t gfp_mask);\n\nstatic inline int\ncall_usermodehelper_fnsB(char *path, char **argv, char **envp,\n int wait, //enum umh_wait wait,\n int (*init)(struct subprocess_info *info),\n void (*cleanup)(struct subprocess_info *), void *data)\n{\n struct subprocess_info *info;\n struct subprocess_infoB *infoB;\n gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;\n int ret;\n\n populate_rootfs_wait();\n\n infoB = call_usermodehelper_setupB(path, argv, envp, gfp_mask);\n printk(KBUILD_MODNAME ":a: pid %d\\n", infoB->pid);\n info = (struct subprocess_info *) infoB;\n\n if (info == NULL)\n return -ENOMEM;\n\n call_usermodehelper_setfns(info, init, cleanup, data);\n printk(KBUILD_MODNAME ":b: pid %d\\n", infoB->pid);\n\n // this must be called first, before infoB->pid is populated (by __call_usermodehelperB):\n ret = call_usermodehelper_exec(info, wait);\n\n // assign global pid (and infoB) here, so rest of the code has it:\n callmodule_pid = infoB->pid;\n callmodule_infoB = infoB; \n printk(KBUILD_MODNAME ":c: pid %d\\n", callmodule_pid);\n\n return ret;\n}\n\nstatic inline int\ncall_usermodehelperB(char *path, char **argv, char **envp, int wait) //enum umh_wait wait)\n{\n return call_usermodehelper_fnsB(path, argv, envp, wait,\n NULL, NULL, NULL);\n}\n\nstatic void __call_usermodehelperB(struct work_struct *work)\n{\n struct subprocess_infoB *sub_infoB =\n container_of(work, struct subprocess_infoB, work);\n int wait = sub_infoB->wait; // enum umh_wait wait = sub_info->wait;\n pid_t pid;\n struct subprocess_info *sub_info;\n // hack - declare function pointers\n int (*ptrwait_for_helper)(void *data);\n int (*ptr____call_usermodehelper)(void *data);\n // assign function pointers to verbatim addresses as obtained from /proc/kallsyms\n int killret;\n struct task_struct *spawned_task;\n ptrwait_for_helper = (void *)0xc1065b60;\n ptr____call_usermodehelper = (void *)0xc1065ed0;\n\n sub_info = (struct subprocess_info *)sub_infoB;\n\n if (wait == UMH_WAIT_PROC)\n pid = kernel_thread((*ptrwait_for_helper), sub_info, //(wait_for_helper, sub_info,\n CLONE_FS | CLONE_FILES | SIGCHLD);\n else\n pid = kernel_thread((*ptr____call_usermodehelper), sub_info, //(____call_usermodehelper, sub_info,\n CLONE_VFORK | SIGCHLD);\n\n spawned_task = pid_task(find_vpid(pid), PIDTYPE_PID);\n\n // stop/suspend/pause task\n killret = kill_pid(find_vpid(pid), SIGSTOP, 1); \n if (spawned_task!=NULL) {\n // does this stop the process really?\n spawned_task->state = __TASK_STOPPED;\n printk(KBUILD_MODNAME ": : exst %d exco %d exsi %d diex %d inex %d inio %d\\n", spawned_task->exit_state, spawned_task->exit_code, spawned_task->exit_signal, spawned_task->did_exec, spawned_task->in_execve, spawned_task->in_iowait);\n }\n printk(KBUILD_MODNAME ": : (kr: %d)\\n", killret);\n printk(KBUILD_MODNAME ": : pid %d (%p) (%s)\\n", pid, spawned_task,\n (spawned_task!=NULL)?((spawned_task->state==-1)?"unrunnable":((spawned_task->state==0)?"runnable":"stopped")):"null" );\n // grab and save the pid (and task_struct) here:\n sub_infoB->pid = pid;\n sub_infoB->task = spawned_task;\n switch (wait) {\n case UMH_NO_WAIT:\n call_usermodehelper_freeinfo(sub_info);\n break;\n case UMH_WAIT_PROC:\n if (pid > 0)\n break;\n /* FALLTHROUGH */\n case UMH_WAIT_EXEC:\n if (pid < 0)\n sub_info->retval = pid;\n complete(sub_info->complete);\n }\n}\n\nstruct subprocess_infoB *call_usermodehelper_setupB(char *path, char **argv,\n char **envp, gfp_t gfp_mask)\n{\n struct subprocess_infoB *sub_infoB;\n sub_infoB = kzalloc(sizeof(struct subprocess_infoB), gfp_mask);\n if (!sub_infoB)\n goto out;\n\n INIT_WORK(&sub_infoB->work, __call_usermodehelperB);\n sub_infoB->path = path;\n sub_infoB->argv = argv;\n sub_infoB->envp = envp;\n out:\n return sub_infoB;\n}\n\n#if TRY_USE_KPROBES\n// copy from /kernel/trace/trace_probe.c (is unexported)\nint traceprobe_command(const char *buf, int (*createfn)(int, char **))\n{\n char **argv;\n int argc, ret;\n\n argc = 0;\n ret = 0;\n argv = argv_split(GFP_KERNEL, buf, &argc);\n if (!argv)\n return -ENOMEM;\n\n if (argc)\n ret = createfn(argc, argv);\n\n argv_free(argv);\n\n return ret;\n}\n\n// copy from kernel/trace/trace_kprobe.c?v=2.6.38 (is unexported)\n#define TP_FLAG_TRACE 1\n#define TP_FLAG_PROFILE 2\ntypedef void (*fetch_func_t)(struct pt_regs *, void *, void *);\nstruct fetch_param {\n fetch_func_t fn;\n void *data;\n};\ntypedef int (*print_type_func_t)(struct trace_seq *, const char *, void *, void *);\nenum {\n FETCH_MTD_reg = 0,\n FETCH_MTD_stack,\n FETCH_MTD_retval,\n FETCH_MTD_memory,\n FETCH_MTD_symbol,\n FETCH_MTD_deref,\n FETCH_MTD_END,\n};\n// Fetch type information table * /\nstruct fetch_type {\n const char *name; /* Name of type */\n size_t size; /* Byte size of type */\n int is_signed; /* Signed flag */\n print_type_func_t print; /* Print functions */\n const char *fmt; /* Fromat string */\n const char *fmttype; /* Name in format file */\n // Fetch functions * /\n fetch_func_t fetch[FETCH_MTD_END];\n};\nstruct probe_arg {\n struct fetch_param fetch;\n struct fetch_param fetch_size;\n unsigned int offset; /* Offset from argument entry */\n const char *name; /* Name of this argument */\n const char *comm; /* Command of this argument */\n const struct fetch_type *type; /* Type of this argument */\n};\nstruct trace_probe {\n struct list_head list;\n struct kretprobe rp; /* Use rp.kp for kprobe use */\n unsigned long nhit;\n unsigned int flags; /* For TP_FLAG_* */\n const char *symbol; /* symbol name */\n struct ftrace_event_class class;\n struct ftrace_event_call call;\n ssize_t size; /* trace entry size */\n unsigned int nr_args;\n struct probe_arg args[];\n};\nstatic int probe_is_return(struct trace_probe *tp)\n{\n return tp->rp.handler != NULL;\n}\nstatic int probe_event_enable(struct ftrace_event_call *call)\n{\n struct trace_probe *tp = (struct trace_probe *)call->data;\n\n tp->flags |= TP_FLAG_TRACE;\n if (probe_is_return(tp))\n return enable_kretprobe(&tp->rp);\n else\n return enable_kprobe(&tp->rp.kp);\n}\n#define KPROBE_EVENT_SYSTEM "kprobes"\n#endif // TRY_USE_KPROBES\n\n// <<<<<<<<<<<<<<<<<<<<<<\n\nstatic struct page *walk_page_table(unsigned long addr, struct task_struct *intask)\n{\n pgd_t *pgd;\n pte_t *ptep, pte;\n pud_t *pud;\n pmd_t *pmd;\n\n struct page *page = NULL;\n struct mm_struct *mm = intask->mm;\n\n callmodule_infoB->last_page_physaddr = 0ULL; // reset here, in case of early exit\n\n printk(KBUILD_MODNAME ": walk_ 0x%lx ", addr);\n\n pgd = pgd_offset(mm, addr);\n if (pgd_none(*pgd) || pgd_bad(*pgd))\n goto out;\n printk(KBUILD_MODNAME ": Valid pgd ");\n\n pud = pud_offset(pgd, addr);\n if (pud_none(*pud) || pud_bad(*pud))\n goto out;\n printk( ": Valid pud");\n\n pmd = pmd_offset(pud, addr);\n if (pmd_none(*pmd) || pmd_bad(*pmd))\n goto out;\n printk( ": Valid pmd");\n\n ptep = pte_offset_map(pmd, addr);\n if (!ptep)\n goto out;\n pte = *ptep;\n\n page = pte_page(pte);\n if (page) {\n callmodule_infoB->last_page_physaddr = (unsigned long long)page_to_phys(page);\n printk( ": page frame struct is @ %p; *virtual (page_address) @ %p (is_vmalloc_addr %d virt_addr_valid %d virt_to_phys 0x%llx) page_to_pfn %lx page_to_phys 0x%llx", page, page_address(page), is_vmalloc_addr((void*)page_address(page)), virt_addr_valid(page_address(page)), (unsigned long long)virt_to_phys(page_address(page)), page_to_pfn(page), callmodule_infoB->last_page_physaddr);\n }\n\n //~ pte_unmap(ptep);\n\nout:\n printk("\\n");\n return page;\n}\n\nstatic void sample_hbp_handler(struct perf_event *bp,\n struct perf_sample_data *data,\n struct pt_regs *regs)\n{\n trace_printk(KBUILD_MODNAME ": hwbp hit: id [%llu]\\n", bp->id );\n //~ unregister_hw_breakpoint(bp);\n}\n\n// ----------------------\n\nstatic int __init callmodule_init(void)\n{\n int ret = 0;\n char userprog[] = "/path/to/wtest";\n char *argv[] = {userprog, "2", NULL };\n char *envp[] = {"HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };\n struct task_struct *p;\n struct task_struct *par;\n struct task_struct *pc;\n struct list_head *children_list_head;\n struct list_head *cchildren_list_head;\n char *state_str;\n unsigned long offset, taddr;\n int (*ptr_create_trace_probe)(int argc, char **argv); \n st
归档时间: |
|
查看次数: |
2311 次 |
最近记录: |