使用ftrace和kprobes捕获用户空间程序集(通过使用虚拟地址转换)?

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日志中.

所以,总结一下:

  • 是从虚拟(从可执行文件的反汇编)地址获取(可能通过单独的内核模块)物理地址的方法 - 因此它们用于触发由ftrace记录的kprobe_event - 值得追求?如果是这样,是否有任何可用于此地址转换目的的内核模块示例?
  • 在执行特定的内存地址时,我是否可以使用内核模块"注册"回调/处理函数?然后我可以简单地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)

sda*_*aau 1

显然,使用 3.5+ 内核上的内置 uprobes 会容易得多;但考虑到我的内核 2.6.38 的 uprobes 是一个非常深入的补丁(我无法真正将其隔离在单独的内核模块中,以避免修补内核),以下是我可以注意到的独立补丁2.6.38 上的模块。(由于我仍然不确定很多事情,我仍然希望看到一个答案来纠正这篇文章中的任何误解。)

\n\n

我想我已经到达了某个地方,但不是kprobes。我不确定,但似乎我设法获得了正确的物理地址;然而,kprobes文档明确指出,当使用“ @ADDR:在ADDR处获取内存(ADDR应该在内核中) ”时;我得到的物理地址低于 0xc0000000 的内核边界(但是,0xc0000000 通常与虚拟内存布局一起?)。

\n\n

所以我使用了一个硬件断点 - 该模块在下面,但是买者自负 - 它的行为是随机的,有时会导致内核哎呀!。通过编译模块并运行bash

\n\n
$ 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对我来说是:

\n\n
$ 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\n
...\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\n
...\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\n

以下是有关该模块的一些注释:

\n\n
    \n
  • 大部分模块来自执行/调用用户空间程序,并从内核模块获取其 pid;启动用户进程并获取 pid\n\n
      \n
    • 因为我们必须通过task_struct 来获取pid;在这里我保存了两者(这有点多余)
    • \n
  • \n
  • 不导出函数符号的地方;如果符号位于kallsyms,那么我使用指向该地址的函数指针;否则其他需要的功能是从源复制的
  • \n
  • 我不知道如何启动停止的用户空间进程,因此在生成后我发出 a SIGSTOP(就其本身而言,此时似乎有点不可靠),并将状态设置为__TASK_STOPPED)。\n\n
      \n
    • 有时我可能仍然会得到“可运行”状态,但有时我不希望它出现 - 但是,如果 init 因错误而提前退出,我注意到在wtest它自然终止后很长时间内仍挂在进程列表中,所以我猜想作品。
    • \n
  • \n
  • 为了获取绝对/物理地址,我使用了Linux 中进程的页表来获取与虚拟地址相对应的页面,然后挖掘我发现的内核源page_to_phys()以获取该地址(内部通过页帧号);LDD3 ch.15 有助于理解 pfn 和物理地址之间的关系。\n\n
      \n
    • 因为在这里我希望有物理地址,所以我不使用 PAGE_SHIFT,而是直接从objdump汇编输出计算偏移量 - 不过,我不能 100% 确定这是正确的。
    • \n
    • 注意,(另请参见如何从 Linux 内核中的任意地址获取 struct page),模块输出表明虚拟地址0x08048000既不是is_vmalloc_addr也不是virt_addr_valid;我想,这应该告诉我,人们不可能既没有使用vmalloc_to_pfn()也没有virt_to_page()到达它的物理地址!?
    • \n
  • \n
  • kprobes从内核空间进行设置ftrace有点棘手(需要复制函数)\n\n
      \n
    • 尝试在我获得的物理地址(例如 0x639ec474)上设置 a kprobe,总是导致“无法插入探针(-22)
    • \n
    • 只是为了看看格式是否被解析,我尝试使用下面kallsyms的函数地址tracing_on()(0xc10bcf60);这似乎有效 - 因为它引发了一个致命的“ BUG:原子调度”(显然,我们不打算在 module_init 中设置断点?)。Bug 是致命的,因为它使kprobes目录从ftrace目录从调试目录
    • \n
    • 仅创建kprobe不会使其出现在ftrace日志中 - 它还需要启用;启用所需的代码就在那里 - 但由于之前的错误,我从未尝试过
    • \n
  • \n
  • 最后,断点设置来自Watch a variable (内存地址)在Linux内核中的变化,并在变化时打印堆栈跟踪?\n\n
      \n
    • 我从未见过设置可执行硬件断点的示例;它对我来说一直失败,直到通过内核源代码搜索,我发现 for HW_BREAKPOINT_Xattr.bp_len需要设置为sizeof(long)
    • \n
    • 如果我尝试从 _init 或处理程序获取printk变量attr- 某些东西会变得严重混乱,无论我接下来尝试打印什么变量,我都会得到它的值 0x5 (或 0x48)(?!)
    • \n
    • 由于我尝试对两个断点使用单个处理程序函数,因此从 _init 到处理程序幸存下来的唯一可靠的信息(能够区分这两个断点)似乎是bp->id
    • \n
    • 这些 id 是自动分配的,如果您取消注册断点,它们似乎不会被重新声明(我不会取消注册它们以避免额外的 ftrace 打印输出)。
    • \n
  • \n
\n\n

就随机性而言,我认为这是因为该过程不是在停止状态下启动的;当它停止时,它最终会处于不同的状态(或者,很可能,我在某处缺少一些锁定)。无论如何,您还可以期待syslog

\n\n
[ 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:

\n\n
[  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干杯!

\n\n

Makefile:

\n\n
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\n

callmodule.c:

\n\n
#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