linux – 使用ftrace和kprobes捕获用户空间程序集(使用虚拟地址
对于冗长的帖子道歉,我在以较短的方式制定它时遇到了麻烦.此外,这可能更适合Unix&
Linux Stack Exchange,但我会先在SO上尝试,因为有一个ftrace标签.
无论如何 – 我想观察使用ftrace在完整的function_graph捕获的上下文中执行用户程序的机器指令.一个问题是我需要这个旧内核: $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 ……在这个版本中,没有UPROBES – 正如Uprobes in 3.5 [LWN.net]所说,它应该能够做到这一点. (只要我不需要修补原始内核,我就会愿意尝试使用树构建的内核模块,正如User-Space Probes (Uprobes) [chunghwan.com]似乎证明的那样;但据我在0: Inode based uprobes [LWN.net]中可以看到,2.6可能需要一个完整的补丁) 但是,在这个版本上,有一个/ sys / kernel / debug / kprobes和/ sys / kernel / debug / tracing / kprobe_events;和Documentation/trace/kprobetrace.txt意味着可以直接在地址上设置kprobe;即使我无法在任何地方找到如何使用它的例子. 在任何情况下,我仍然不确定使用什么地址 – 作为一个小例子,假设我想跟踪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 ' 您可以在debugging – Observing a hard-disk write in kernel space (with drivers/modules) – Unix & Linux Stack Exchange(我从中获得示例)中看到(否则是复杂的)结果ftrace日志的一部分. 基本上,我想在这个ftrace日志中打印输出,当主要的第一条指令 – 比如0x8048474,0x8048475,0x8048477,0x804847a,0x804847d,0x8048483和0x8048487的指令 – 由(任何)CPU执行时.问题是,据我所知,从Anatomy of a Program in Memory : Gustavo Duarte开始,这些地址就是虚拟地址,从过程本身的角度来看(我收集的是,相同的视角由/ proc / PID / maps显示)……显然,对于krpobe_event,我需要一个物理地址? 所以,我的想法是:如果我能找到对应于程序反汇编的虚拟地址的物理地址(比如编写一个内核模块,它可以接受pid和地址,并通过procfs返回物理地址),我可以设置通过上面脚本中的/ sys / kernel / debug / tracing / kprobe_events将地址作为一种“跟踪点” – 并希望将它们放在ftrace日志中.原则上这可行吗? 我在Linux(ubuntu),C language: Virtual to Physical Address Translation – Stack Overflow找到了一个问题:
但是,vmalloc_to_pfn似乎也不是微不足道的: x86 64 – vmalloc_to_pfn returns 32 bit address on Linux 32 system. Why does it chop off higher bits of PAE physical address? – Stack Overflow
所以,我不确定我是如何可靠地提取物理地址所以它们被kprobes追踪 – 特别是因为“它甚至可以在任何时候改变”.但是在这里,我希望由于程序很小而且微不足道,程序在跟踪时不会交换,从而可以获得适当的捕获. (所以即使我必须多次运行调试脚本,只要我希望在10次(甚至100次)中获得“正确”捕获,我就可以了.) 请注意,我希望通过ftrace输出,以便时间戳在同一个域中表示(有关时间戳问题的说明,请参阅Reliable Linux kernel timestamps (or adjustment thereof) with both usbmon and ftrace? – Stack Overflow).因此,即使我能想出一个gdb脚本,从用户空间运行和跟踪程序(同时获得ftrace捕获) – 我想避免这种情况,因为gdb本身的开销会显示在ftrace日志中. 所以,总结一下: >是否从虚拟(从可执行文件的反汇编)地址获取(可能通过单独的内核模块)物理地址的方法 – 因此它们用于触发由ftrace记录的kprobe_event – 值得追求?如果是这样,是否有任何可用于此地址转换目的的内核模块示例? 实际上,在2007年发布的Jim Keniston – utrace-based uprobes: systemtap mailing list中,有11个Uprobes示例(添加到Documentation / uprobes.txt中),这似乎就是一个内核模块注册处理函数.不幸的是,它使用linux / uprobes.h;我的/usr/src/linux-headers-2.6.38-16/include/linux/中只有kprobes.h.另外,在我的系统上,甚至systemtap抱怨没有启用CONFIG_UTRACE(参见this comment)…所以如果有任何其他方法我可以用来获得我想要的调试跟踪,而不必重新编译内核来获取探测器,它很高兴知道…… 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; } 解决方法
显然,使用内核3.5上的内置增强功能会更容易;但鉴于我的内核2.6.38的uprobes是一个非常深入的补丁(我无法在单独的内核模块中真正隔离,以避免修补内核),这是我可以注意到的独立模块在2.6.38. (由于我仍然不确定很多事情,我仍然希望看到一个可以纠正这篇文章中任何误解的答案.)
我想我到了某个地方,但没有kprobes.我不确定,但似乎我设法让物理地址正确;但是,kprobes文档是特定的,当使用“@ADDR:在ADDR获取内存(ADDR应该在内核中)”时;我得到的物理地址低于0xc0000000的内核边界(但是,0xc0000000通常与虚拟内存布局一起?). 所以我使用了硬件断点 – 模块在下面,但是需要注意的是 – 它随机行为,偶尔会导致内核哎呀!通过编译模块,并在bash中运行: $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 8192 > $KDBGPATH/buffer_size_kb ; echo 0 > $KDBGPATH/tracing_on ; echo > $KDBGPATH/trace' $sudo insmod ./callmodule.ko && sleep 0.1 && sudo rmmod callmodule && tail -n25 /var/log/syslog | tee log.txt && sudo cat /sys/kernel/debug/tracing/trace >> log.txt ……我得到一份日志.我想跟踪wtest main()的前两个指令,对我来说是: $objdump -S wtest/wtest | grep -A3 'int main' int main(void) { 8048474: 55 push %ebp 8048475: 89 e5 mov %esp,%ebp 8048477: 83 e4 f0 and $0xfffffff0,%esp …在虚拟地址0x08048474和0x08048475.在syslog输出中,我可以得到,说: ... [ 1106.383011] callmodule: parent task a: f40a9940 c: kworker/u:1 p: [14] s: stopped [ 1106.383017] callmodule: - wtest [9404] [ 1106.383023] callmodule: Trying to walk page table; addr task 0xEAE90CA0 ->mm ->start_code: 0x08048000 ->end_code: 0x080485F4 [ 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 [ 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 [ 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 [ 1106.383083] callmodule: physaddr : (0x080483c0 ->) 0x639ec3c0 : (0x08048474 ->) 0x639ec474 [ 1106.383106] callmodule: 0x08048474 id [3] [ 1106.383113] callmodule: 0x08048475 id [4] [ 1106.383118] callmodule: (( 0x08048000 is_vmalloc_addr 0 virt_addr_valid 0 )) [ 1106.383130] callmodule: cont pid task a: eae90ca0 c: wtest p: [9404] s: runnable [ 1106.383147] initcall callmodule_init+0x0/0x1000 [callmodule] returned with preemption imbalance [ 1106.518074] callmodule: < exit …意味着它将虚拟地址0x08048474映射到物理地址0x639ec474.但是,物理不用于硬件断点 – 我们可以直接向register_user_hw_breakpoint提供虚拟地址;但是,我们还需要提供该过程的task_struct.有了这个,我可以在ftrace输出中得到这样的东西: ... 597.907256 | 1) wtest-5339 | | handle_mm_fault() { ... 597.907310 | 1) wtest-5339 | + 35.627 us | } 597.907311 | 1) wtest-5339 | + 46.245 us | } 597.907312 | 1) wtest-5339 | + 56.143 us | } 597.907313 | 1) wtest-5339 | 1.039 us | up_read(); 597.907317 | 1) wtest-5339 | 1.285 us | native_get_debugreg(); 597.907319 | 1) wtest-5339 | 1.075 us | native_set_debugreg(); 597.907322 | 1) wtest-5339 | 1.129 us | native_get_debugreg(); 597.907324 | 1) wtest-5339 | 1.189 us | native_set_debugreg(); 597.907329 | 1) wtest-5339 | | () { 597.907333 | 1) wtest-5339 | | /* callmodule: hwbp hit: id [3] */ 597.907334 | 1) wtest-5339 | 5.567 us | } 597.907336 | 1) wtest-5339 | 1.123 us | native_set_debugreg(); 597.907339 | 1) wtest-5339 | 1.130 us | native_get_debugreg(); 597.907341 | 1) wtest-5339 | 1.075 us | native_set_debugreg(); 597.907343 | 1) wtest-5339 | 1.075 us | native_get_debugreg(); 597.907345 | 1) wtest-5339 | 1.081 us | native_set_debugreg(); 597.907348 | 1) wtest-5339 | | () { 597.907350 | 1) wtest-5339 | | /* callmodule: hwbp hit: id [4] */ 597.907351 | 1) wtest-5339 | 3.033 us | } 597.907352 | 1) wtest-5339 | 1.105 us | native_set_debugreg(); 597.907358 | 1) wtest-5339 | 1.315 us | down_read_trylock(); 597.907360 | 1) wtest-5339 | 1.123 us | _cond_resched(); 597.907362 | 1) wtest-5339 | 1.027 us | find_vma(); 597.907364 | 1) wtest-5339 | | handle_mm_fault() { ... …对应于程序集的跟踪由断点ID标记.值得庆幸的是,正如预期的那样,它们是正确的;但是,ftrace还在其间捕获了一些调试命令.无论如何,这是我想看到的. 以下是有关该模块的一些注意事项: >大部分模块来自Execute/invoke user-space program,and get its pid,from a kernel module;启动用户进程并获取pid的位置 >因为我们必须到task_struct去到pid;在这里我保存两者(这是多余的) >不输出功能符号的地方;如果符号是kallsyms,那么我使用一个指向地址的函数指针;否则从源复制其他所需的功能 >我可能仍然会获得状态“runnable”,有时我不期望它 – 但是,如果init早期退出并出现错误,我已经注意到wtest会在进程列表中挂起很久就会自然终止,所以我猜作品. >为了获得绝对/物理地址,我使用Walking page tables of a process in Linux到达对应于虚拟地址的页面,然后挖掘内核源代码,我发现page_to_phys()到达地址(内部通过页面帧号); LDD3 ch.15有助于理解pfn和物理地址之间的关系. >从这里我希望有物理地址,我不使用PAGE_SHIFT,但直接从objdump的汇编输出计算偏移量 – 我不是100%确定这是正确的. >从内核空间为ftrace设置kprobes有点棘手(需要复制函数) >尝试在我得到的物理地址上设置kprobe(例如0x639ec474),总是会产生“无法插入探测器(-22)” >最后,断点设置是从Watch a variable (memory address) change in Linux kernel,and print stack trace when it changes?开始 >我从未见过设置可执行硬件断点的示例;它一直对我失败,直到通过内核源搜索,我发现对于HW_BREAKPOINT_X,attr.bp_len需要设置为sizeof(long) 就随机性而言,我认为这是因为该过程不是在停止状态下开始的;当它停止时,它会以不同的状态结束(或者很可能,我在某个地方错过了一些锁定).无论如何,你也可以在syslog中期待: [ 1661.815114] callmodule: Trying to walk page table; addr task 0xEAF68CA0 ->mm ->start_code: 0x08048000 ->end_code: 0x080485F4 [ 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 [ 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 [ 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 …即使使用适当的任务指针(通过start_code判断),也只获得0x0作为物理地址.有时你得到相同的结果,但是使用start_code:0x00000000 – > end_code:0x00000000.有时,即使pid可以,也无法获取task_struct: [ 833.380417] callmodule:c: pid 7663 [ 833.380424] callmodule: everything all right; pid 7663 (7663) [ 833.380430] callmodule: p is NULL - exiting [ 833.516160] callmodule: < exit 好吧,希望有人会评论并澄清这个模块的一些行为:) Makefile文件: EXTRA_CFLAGS=-g -O0 obj-m += callmodule.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean callmodule.c: #include <linux/module.h> #include <linux/slab.h> //kzalloc #include <linux/syscalls.h> // SIGCHLD,... sys_wait4,... #include <linux/kallsyms.h> // kallsyms_lookup,print_symbol #include <linux/highmem.h> // ‘kmap_atomic’ (via pte_offset_map) #include <asm/io.h> // page_to_phys (arch/x86/include/asm/io.h) struct subprocess_infoB; // forward declare // global variable - to avoid intervening too much in the return of call_usermodehelperB: static int callmodule_pid; static struct subprocess_infoB* callmodule_infoB; #define TRY_USE_KPROBES 0 // 1 // enable/disable kprobes usage code #include <linux/kprobes.h> // enable_kprobe // for hardware breakpoint: #include <linux/perf_event.h> #include <linux/hw_breakpoint.h> // define a modified struct (with extra fields) here: struct subprocess_infoB { struct work_struct work; struct completion *complete; char *path; char **argv; char **envp; int wait; //enum umh_wait wait; int retval; int (*init)(struct subprocess_info *info); void (*cleanup)(struct subprocess_info *info); void *data; pid_t pid; struct task_struct *task; unsigned long long last_page_physaddr; }; struct subprocess_infoB *call_usermodehelper_setupB(char *path,char **argv,char **envp,gfp_t gfp_mask); static inline int call_usermodehelper_fnsB(char *path,int wait,//enum umh_wait wait,int (*init)(struct subprocess_info *info),void (*cleanup)(struct subprocess_info *),void *data) { struct subprocess_info *info; struct subprocess_infoB *infoB; gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; int ret; populate_rootfs_wait(); infoB = call_usermodehelper_setupB(path,argv,envp,gfp_mask); printk(KBUILD_MODNAME ":a: pid %dn",infoB->pid); info = (struct subprocess_info *) infoB; if (info == NULL) return -ENOMEM; call_usermodehelper_setfns(info,init,cleanup,data); printk(KBUILD_MODNAME ":b: pid %dn",infoB->pid); // this must be called first,before infoB->pid is populated (by __call_usermodehelperB): ret = call_usermodehelper_exec(info,wait); // assign global pid (and infoB) here,so rest of the code has it: callmodule_pid = infoB->pid; callmodule_infoB = infoB; printk(KBUILD_MODNAME ":c: pid %dn",callmodule_pid); return ret; } static inline int call_usermodehelperB(char *path,int wait) //enum umh_wait wait) { return call_usermodehelper_fnsB(path,wait,NULL,NULL); } static void __call_usermodehelperB(struct work_struct *work) { struct subprocess_infoB *sub_infoB = container_of(work,struct subprocess_infoB,work); int wait = sub_infoB->wait; // enum umh_wait wait = sub_info->wait; pid_t pid; struct subprocess_info *sub_info; // hack - declare function pointers int (*ptrwait_for_helper)(void *data); int (*ptr____call_usermodehelper)(void *data); // assign function pointers to verbatim addresses as obtained from /proc/kallsyms int killret; struct task_struct *spawned_task; ptrwait_for_helper = (void *)0xc1065b60; ptr____call_usermodehelper = (void *)0xc1065ed0; sub_info = (struct subprocess_info *)sub_infoB; if (wait == UMH_WAIT_PROC) pid = kernel_thread((*ptrwait_for_helper),sub_info,//(wait_for_helper,CLONE_FS | CLONE_FILES | SIGCHLD); else pid = kernel_thread((*ptr____call_usermodehelper),//(____call_usermodehelper,CLONE_VFORK | SIGCHLD); spawned_task = pid_task(find_vpid(pid),PIDTYPE_PID); // stop/suspend/pause task killret = kill_pid(find_vpid(pid),SIGSTOP,1); if (spawned_task!=NULL) { // does this stop the process really? spawned_task->state = __TASK_STOPPED; printk(KBUILD_MODNAME ": : exst %d exco %d exsi %d diex %d inex %d inio %dn",spawned_task->exit_state,spawned_task->exit_code,spawned_task->exit_signal,spawned_task->did_exec,spawned_task->in_execve,spawned_task->in_iowait); } printk(KBUILD_MODNAME ": : (kr: %d)n",killret); printk(KBUILD_MODNAME ": : pid %d (%p) (%s)n",pid,spawned_task,(spawned_task!=NULL)?((spawned_task->state==-1)?"unrunnable":((spawned_task->state==0)?"runnable":"stopped")):"null" ); // grab and save the pid (and task_struct) here: sub_infoB->pid = pid; sub_infoB->task = spawned_task; switch (wait) { case UMH_NO_WAIT: call_usermodehelper_freeinfo(sub_info); break; case UMH_WAIT_PROC: if (pid > 0) break; /* FALLTHROUGH */ case UMH_WAIT_EXEC: if (pid < 0) sub_info->retval = pid; complete(sub_info->complete); } } struct subprocess_infoB *call_usermodehelper_setupB(char *path,gfp_t gfp_mask) { struct subprocess_infoB *sub_infoB; sub_infoB = kzalloc(sizeof(struct subprocess_infoB),gfp_mask); if (!sub_infoB) goto out; INIT_WORK(&sub_infoB->work,__call_usermodehelperB); sub_infoB->path = path; sub_infoB->argv = argv; sub_infoB->envp = envp; out: return sub_infoB; } #if TRY_USE_KPROBES // copy from /kernel/trace/trace_probe.c (is unexported) int traceprobe_command(const char *buf,int (*createfn)(int,char **)) { char **argv; int argc,ret; argc = 0; ret = 0; argv = argv_split(GFP_KERNEL,buf,&argc); if (!argv) return -ENOMEM; if (argc) ret = createfn(argc,argv); argv_free(argv); return ret; } // copy from kernel/trace/trace_kprobe.c?v=2.6.38 (is unexported) #define TP_FLAG_TRACE 1 #define TP_FLAG_PROFILE 2 typedef void (*fetch_func_t)(struct pt_regs *,void *,void *); struct fetch_param { fetch_func_t fn; void *data; }; typedef int (*print_type_func_t)(struct trace_seq *,const char *,void *); enum { FETCH_MTD_reg = 0,FETCH_MTD_stack,FETCH_MTD_retval,FETCH_MTD_memory,FETCH_MTD_symbol,FETCH_MTD_deref,FETCH_MTD_END,}; // Fetch type information table * / struct fetch_type { const char *name; /* Name of type */ size_t size; /* Byte size of type */ int is_signed; /* Signed flag */ print_type_func_t print; /* Print functions */ const char *fmt; /* Fromat string */ const char *fmttype; /* Name in format file */ // Fetch functions * / fetch_func_t fetch[FETCH_MTD_END]; }; struct probe_arg { struct fetch_param fetch; struct fetch_param fetch_size; unsigned int offset; /* Offset from argument entry */ const char *name; /* Name of this argument */ const char *comm; /* Command of this argument */ const struct fetch_type *type; /* Type of this argument */ }; struct trace_probe { struct list_head list; struct kretprobe rp; /* Use rp.kp for kprobe use */ unsigned long nhit; unsigned int flags; /* For TP_FLAG_* */ const char *symbol; /* symbol name */ struct ftrace_event_class class; struct ftrace_event_call call; ssize_t size; /* trace entry size */ unsigned int nr_args; struct probe_arg args[]; }; static int probe_is_return(struct trace_probe *tp) { return tp->rp.handler != NULL; } static int probe_event_enable(struct ftrace_event_call *call) { struct trace_probe *tp = (struct trace_probe *)call->data; tp->flags |= TP_FLAG_TRACE; if (probe_is_return(tp)) return enable_kretprobe(&tp->rp); else return enable_kprobe(&tp->rp.kp); } #define KPROBE_EVENT_SYSTEM "kprobes" #endif // TRY_USE_KPROBES // <<<<<<<<<<<<<<<<<<<<<< static struct page *walk_page_table(unsigned long addr,struct task_struct *intask) { pgd_t *pgd; pte_t *ptep,pte; pud_t *pud; pmd_t *pmd; struct page *page = NULL; struct mm_struct *mm = intask->mm; callmodule_infoB->last_page_physaddr = 0ULL; // reset here,in case of early exit printk(KBUILD_MODNAME ": walk_ 0x%lx ",addr); pgd = pgd_offset(mm,addr); if (pgd_none(*pgd) || pgd_bad(*pgd)) goto out; printk(KBUILD_MODNAME ": Valid pgd "); pud = pud_offset(pgd,addr); if (pud_none(*pud) || pud_bad(*pud)) goto out; printk( ": Valid pud"); pmd = pmd_offset(pud,addr); if (pmd_none(*pmd) || pmd_bad(*pmd)) goto out; printk( ": Valid pmd"); ptep = pte_offset_map(pmd,addr); if (!ptep) goto out; pte = *ptep; page = pte_page(pte); if (page) { callmodule_infoB->last_page_physaddr = (unsigned long long)page_to_phys(page); 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); } //~ pte_unmap(ptep); out: printk("n"); return page; } static void sample_hbp_handler(struct perf_event *bp,struct perf_sample_data *data,struct pt_regs *regs) { trace_printk(KBUILD_MODNAME ": hwbp hit: id [%llu]n",bp->id ); //~ unregister_hw_breakpoint(bp); } // ---------------------- static int __init callmodule_init(void) { int ret = 0; char userprog[] = "/path/to/wtest"; char *argv[] = {userprog,"2",NULL }; char *envp[] = {"HOME=/","PATH=/sbin:/usr/sbin:/bin:/usr/bin",NULL }; struct task_struct *p; struct task_struct *par; struct task_struct *pc; struct list_head *children_list_head; struct list_head *cchildren_list_head; char *state_str; unsigned long offset,taddr; int (*ptr_create_trace_probe)(int argc,char **argv); struct trace_probe* (*ptr_find_probe_event)(const char *event,const char *group); //int (*ptr_probe_event_enable)(struct ftrace_event_call *call); // not exported,copy #if TRY_USE_KPROBES char trcmd[256] = ""; struct trace_probe *tp; #endif //TRY_USE_KPROBES struct perf_event *sample_hbp,*sample_hbpb; struct perf_event_attr attr,attrb; printk(KBUILD_MODNAME ": > init %sn",userprog); ptr_create_trace_probe = (void *)0xc10d5120; ptr_find_probe_event = (void *)0xc10d41e0; print_symbol(KBUILD_MODNAME ": symbol @ 0xc1065b60 is %sn",0xc1065b60); // shows wait_for_helper+0x0/0xb0 print_symbol(KBUILD_MODNAME ": symbol @ 0xc1065ed0 is %sn",0xc1065ed0); // shows ____call_usermodehelper+0x0/0x90 print_symbol(KBUILD_MODNAME ": symbol @ 0xc10d5120 is %sn",0xc10d5120); // shows create_trace_probe+0x0/0x590 ret = call_usermodehelperB(userprog,UMH_WAIT_EXEC); if (ret != 0) printk(KBUILD_MODNAME ": error in call to usermodehelper: %in",ret); else printk(KBUILD_MODNAME ": everything all right; pid %d (%d)n",callmodule_pid,callmodule_infoB->pid); tracing_on(); // earlier,so trace_printk of handler is caught! // find the task: rcu_read_lock(); p = pid_task(find_vpid(callmodule_pid),PIDTYPE_PID); rcu_read_unlock(); if (p == NULL) { printk(KBUILD_MODNAME ": p is NULL - exitingn"); return 0; } state_str = (p->state==-1)?"unrunnable":((p->state==0)?"runnable":"stopped"); printk(KBUILD_MODNAME ": pid task a: %p c: %s p: [%d] s: %sn",p,p->comm,p->pid,state_str); // find parent task: par = p->parent; if (par == NULL) { printk(KBUILD_MODNAME ": par is NULL - exitingn"); return 0; } state_str = (par->state==-1)?"unrunnable":((par->state==0)?"runnable":"stopped"); printk(KBUILD_MODNAME ": parent task a: %p c: %s p: [%d] s: %sn",par,par->comm,par->pid,state_str); // iterate through parent's (and our task's) child processes: rcu_read_lock(); // read_lock(&tasklist_lock); list_for_each(children_list_head,&par->children){ p = list_entry(children_list_head,struct task_struct,sibling); printk(KBUILD_MODNAME ": - %s [%d] n",p->pid); if (p->pid == callmodule_pid) { list_for_each(cchildren_list_head,&p->children){ pc = list_entry(cchildren_list_head,sibling); printk(KBUILD_MODNAME ": - - %s [%d] n",pc->comm,pc->pid); } } } rcu_read_unlock(); //~ read_unlock(&tasklist_lock); // NOTE: here p == callmodule_infoB->task !! printk(KBUILD_MODNAME ": Trying to walk page table; addr task 0x%X ->mm ->start_code: 0x%08lX ->end_code: 0x%08lX n",(unsigned int) callmodule_infoB->task,callmodule_infoB->task->mm->start_code,callmodule_infoB->task->mm->end_code); walk_page_table(0x08048000,callmodule_infoB->task); // 080483c0 is start of .text; 08048474 start of main; for objdump -S wtest walk_page_table(0x080483c0,callmodule_infoB->task); walk_page_table(0x08048474,callmodule_infoB->task); if (callmodule_infoB->last_page_physaddr != 0ULL) { printk(KBUILD_MODNAME ": physaddr "); taddr = 0x080483c0; // .text offset = taddr - callmodule_infoB->task->mm->start_code; printk(": (0x%08lx ->) 0x%08llx ",taddr,callmodule_infoB->last_page_physaddr+offset); taddr = 0x08048474; // main offset = taddr - callmodule_infoB->task->mm->start_code; printk(": (0x%08lx ->) 0x%08llx ",callmodule_infoB->last_page_physaddr+offset); printk("n"); #if TRY_USE_KPROBES // can't use this here (BUG: scheduling while atomic,if probe inserts) //~ sprintf(trcmd,"p:myprobe 0x%08llx",callmodule_infoB->last_page_physaddr+offset); // try symbol for c10bcf60 - tracing_on sprintf(trcmd,(unsigned long long)0xc10bcf60); ret = traceprobe_command(trcmd,ptr_create_trace_probe); //create_trace_probe); printk("%s -- ret: %dn",trcmd,ret); // try find probe and enable it (compiles,but untested): tp = ptr_find_probe_event("myprobe",KPROBE_EVENT_SYSTEM); if (tp != NULL) probe_event_enable(&tp->call); #endif //TRY_USE_KPROBES } hw_breakpoint_init(&attr); attr.bp_len = sizeof(long); //HW_BREAKPOINT_LEN_1; attr.bp_type = HW_BREAKPOINT_X ; attr.bp_addr = 0x08048474; // main sample_hbp = register_user_hw_breakpoint(&attr,(perf_overflow_handler_t)sample_hbp_handler,p); printk(KBUILD_MODNAME ": 0x08048474 id [%llu]n",sample_hbp->id); // if (IS_ERR((void __force *)sample_hbp)) { int ret = PTR_ERR((void __force *)sample_hbp); printk(KBUILD_MODNAME ": Breakpoint registration failed (%d)n",ret); //~ return ret; } hw_breakpoint_init(&attrb); attrb.bp_len = sizeof(long); attrb.bp_type = HW_BREAKPOINT_X ; attrb.bp_addr = 0x08048475; // first instruction after main sample_hbpb = register_user_hw_breakpoint(&attrb,p); printk(KBUILD_MODNAME ": 0x08048475 id [%llu]n",sample_hbpb->id); //45 if (IS_ERR((void __force *)sample_hbpb)) { int ret = PTR_ERR((void __force *)sample_hbpb); printk(KBUILD_MODNAME ": Breakpoint registration failed (%d)n",ret); //~ return ret; } printk(KBUILD_MODNAME ": (( 0x08048000 is_vmalloc_addr %d virt_addr_valid %d ))n",is_vmalloc_addr((void*)0x08048000),virt_addr_valid(0x08048000)); kill_pid(find_vpid(callmodule_pid),SIGCONT,1); // resume/continue/restart task state_str = (p->state==-1)?"unrunnable":((p->state==0)?"runnable":"stopped"); printk(KBUILD_MODNAME ": cont pid task a: %p c: %s p: [%d] s: %sn",state_str); return 0; } static void __exit callmodule_exit(void) { tracing_off(); //corresponds to the user space /sys/kernel/debug/tracing/tracing_on file printk(KBUILD_MODNAME ": < exitn"); } module_init(callmodule_init); module_exit(callmodule_exit); MODULE_LICENSE("GPL"); (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |