Revision: 1679 Author: teawater Date: Thu Aug 29 00:29:29 2013 UTC Log: Test Localizing Wiki Content http://code.google.com/p/kgtp/source/detail?r=1679 Added: /wiki/zh-Hans /wiki/zh-Hans/HOWTOCN.wiki ======================================= --- /dev/null +++ /wiki/zh-Hans/HOWTOCN.wiki Thu Aug 29 00:29:29 2013 UTC @@ -0,0 +1,2124 @@ +#labels Phase-QA,Phase-Deploy +Update in 2013-08-28 +<wiki:toc max_depth="5" /> += 关于本文 =+[https://code.google.com/p/kgtp/wiki/HOWTOCN] 是HTML格式的本文最后版本。 <br> +[https://raw.github.com/teawater/kgtp/master/kgtpcn.pdf] 是PDF格式的本文最 后版本。<br> +[https://raw.github.com/teawater/kgtp/release/kgtpcn.pdf] 是PDF格式的本文最 后发布版本。<br>
+= 什么是KGTP =+*KGTP* 是一个 灵活,轻量级,实时 *Linux* (包括 *Android*) *调试器* 和 *跟 踪器* 。<br> +使用KGTP *不需要* 在Linux内核上打PATCH或者重新编译,只要编译KGTP模块并 insmod就可以。<br>
+<br>+其让Linux内核提供一个远程GDB调试接口,于是在本地或者远程的主机上的GDB可以在 不需要停止内核的情况下用GDB tracepoint和其他一些功能 *调试* 和 *跟踪* Linux内核和应用程序。<br> +即使板子上没有GDB而且其没有可用的远程接口,KGTP也可以用离线调试的功能调试内 核(见 [http://code.google.com/p/kgtp/wiki/HOWTOCN#/sys/kernel/debug/gtpframe和离线 调试])。<br><br>
+ +KGTP支持 *X86-32* , *X86-64* , *MIPS* 和 *ARM* 。<br> +KGTP在Linux内核 *2.6.18到upstream* 上都被测试过。<br><br> + +*KGTP新用户可以去看一下 [Quickstart] 。* + +* 请到 [UPDATE] 去看KGTP的更新信息。* + += 需要帮助或者汇报问题 =+你可以把问题发到 [http://code.google.com/p/kgtp/issues/list] 或者写信到 kgtp@xxxxxxxxxxxxx 或者写信到 teawater@xxxxxxxxx 。<br>
+KGTP小组将尽全力帮助你。 + += GDB调试普通程序和KGTP的区别表 = +这个表是给在使用过GDB调试程序的人准备的,他可以帮助你理解和记住KGTP的功能。 +|| *功能* || *GDB调试普通程序* || *GDB控制KGTP调试Linux内核* ||+|| 准备工作 || 系统里安装了GDB。<br>程序用 "-g"选项编译。 || 因为使用了一些 GDB中的新功能,所以KGTP需要和GDB 7.6或者更新的版本。如果你的系统不提供这么新 版本的GDB,你可以到[http://code.google.com/p/gdbt/]取得新版本GDB。同时你可以 在这里取得一步一步编译新版本GDB的介绍。<br>你还需要做一些Linux内核和KGTP的准 备工作,请到 [http://code.google.com/p/kgtp/wiki/HOWTOCN#使用KGTP前的准备工 作] 取得如果做的介绍。 || +|| Attach || 使用命令"gdb -p pid"或者GDB命令"attach pid"可以attach系统中的 某个程序. || 需要先insmod gtp.ko,请看 [https://code.google.com/p/kgtp/wiki/HOWTOCN#如何让GDB连接KGTP]。<br>然后让 GDB连接KGTP,请看[https://code.google.com/p/kgtp/wiki/HOWTOCN#让GDB连接到 KGTP]。<br>请 *注意* GDB连接到KGTP以后,Linux内核不会停止。|| +|| Breakpoints || GDB命令"b place_will_stop",让程序在执行这个命令后执 行,则程序将停止在设置这个断点的地方。|| KGTP不支持断点但是支持tracepoint。 Tracepoints可以被看作一种特殊的断点。其可以设置在Linux kernel中的一些地方然 后定义一些命令到它的action中。当tracepoint开始的时候,他们将会在内核执行到这 些地方的时候执行这些命令。当tracepoint停止的时候,你可以像断点停止程序后你做 的那样用GDB命令分析tracepoint得到的数据。 *区别* 是断点会停止程序但是KGTP中 的tracepoint不会。 请到 [https://code.google.com/p/kgtp/wiki/HOWTOCN#GDB_tracepoint] 看如何使用它。 || +|| 读Memory || GDB停止程序后(也许不需要),它可以用GDB命令"print"或者"x"等应 用程序的内存。 || 你可以在tracepoint中设置特殊的action收集内存到 traceframe中,在tracepoint停止后取得他们的值。 [https://code.google.com/p/kgtp/wiki/HOWTOCN#collect_expr1,_expr2,_...] [http://code.google.com/p/kgtp/wiki/HOWTOCN#用tfind选择trace帧缓存里面的条目 ] <br>或者你可以在内核或者应用程序执行的时候直接读他们的内存。 [https://code.google.com/p/kgtp/wiki/HOWTOCN#在普通模式直接访问当前值] || +|| Step 和 continue || GDB可以用命令"continue"继续程序的执行,用CTRL-C停止 其。|| KGTP不会停止Linux内核,但是tracepoint可以开始和停止。 [https://code.google.com/p/kgtp/wiki/HOWTOCN#启动和停止_tracepoint] <br> 或 者用 while-stepping tracepoint记录一定次数的single-stepping然后让KGTP切换到 回放模式。这样其就支持执行和方向执行命令了。 [https://code.google.com/p/kgtp/wiki/HOWTOCN#使用while-stepping让Linux内核做 单步] || +|| Backtrace || GDB可以用命令"backtrace"打印全部调用栈。|| KGTP也可以。 [https://code.google.com/p/kgtp/wiki/HOWTOCN#如何_backtrace_(stack_dump)]|| +|| Watchpoint || GDB可以用watchpoint让程序在某些内存访问发生的时候停止。|| KGTP可以用watch tracepoint记录内存访问。 [https://code.google.com/p/kgtp/wiki/HOWTOCN#如何用watch_tracepoint控制硬件 断点记录内存访问] || +|| 调用函数 || GDB可以用命令"call function(xx,xx)"调用程序中的函数。 || KGTP可以用插件调用内核中的函数。 [https://code.google.com/p/kgtp/wiki/HOWTOCN#如何增加用C写的插件] ||
+ += 使用KGTP前的准备工作 = + +== Linux内核 == +=== 如果你的系统内核是自己编译的 === +要使用KGTP,你需要打开下面这些内核选项: +{{{ +General setup ---> + [*] Kprobes + +[*] Enable loadable module support ---> + +Kernel hacking ---> + [*] Debug Filesystem + [*] Compile the kernel with debug info +}}} +如果你改了Linux内核config的任何项目,请重新编译你的内核。 + +=== 如果是Android内核 ===+默认的Android Linux内核config应该不支持KGTP。要使用KGTP,你需要打开下面这些 内核选项:
+{{{ +[*] Enable loadable module support ---> +General setup ---> + [*] Prompt for development and/or incomplete code/drivers + [*] Kprobes +Kernel hacking ---> + [*] Debug Filesystem + [*] Compile the kernel with debug info +}}} +如果你改了Linux内核config的任何项目,请重新编译你的内核。 + +=== 如果你的系统内核是发行版自带的 === +你需要安装一些Linux内核软件包。 + +==== Ubuntu ==== +===== 安装Linux内核调试镜像的标准方法 ===== +*1)* 增加调试源到Ubuntu源列表。 + * 在命令行按照下面的命令创建文件 /etc/apt/sources.list.d/ddebs.list: +{{{+echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse" | \
+sudo tee -a /etc/apt/sources.list.d/ddebs.list +}}} + + * 稳定版本(不能是alpha或者betas)需要用命令行增加下面几行: +{{{+echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse +deb http://ddebs.ubuntu.com $(lsb_release -cs)-security main restricted universe multiverse +deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \
+sudo tee -a /etc/apt/sources.list.d/ddebs.list +}}} + + * 导入调试符号签名key: +{{{ +sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 428D7C01 +}}} + + * 运行: +{{{ +sudo apt-get update +}}} +<br> +*2)* 安装Linux内核调试镜像 +{{{ +sudo apt-get install linux-image-$(uname -r)-dbgsym +}}} +于是你可以在"/usr/lib/debug/boot/vmlinux-$(uname -r)"找到内核调试镜像。<br> +请 *注意* 当内核更新的时候这一步 *安装Linux内核调试镜像* 需要再做一次。 + +===== 安装Linux内核调试镜像的第二方法 ===== +如果用标准方法出现问题,请用下面这些命令安装Linux内核调试镜像。 +{{{+wget http://ddebs.ubuntu.com/pool/main/l/linux/linux-image-$(uname -r)-dbgsym_$(dpkg -s linux-image-$(uname -r) | grep ^Version: | sed 's/Version: //')_$(uname -i | sed 's/x86_64/amd64/').ddeb +sudo dpkg -i linux-image-$(uname -r)-dbgsym_$(dpkg -s linux-image-$(uname -r) | grep ^Version: | sed 's/Version: //')_$(uname -i | sed 's/x86_64/amd64/').ddeb
+}}} +请 *注意* 当内核更新的时候这个方法需要重新做一次。 + +===== 安装内核头文件包 ===== +{{{ +sudo apt-get install linux-headers-generic +}}} + +===== 安装内核源码 ===== +====== 新方法 ====== +安装需要的软件包: +{{{ +sudo apt-get install dpkg-dev +}}} +<br> + +取得源码: +{{{ +apt-get source linux-image-$(uname -r) +}}} +则在当前目录找到内核源码目录。 +<br> + +移动这个目录到"/build/buildd/"中。 +====== 老方法 ====== + * 安装源码包: +{{{ +sudo apt-get install linux-source +}}} + * 解压缩源码: +{{{ +sudo mkdir -p /build/buildd/+sudo tar vxjf /usr/src/linux-source-$(uname -r | sed 's/-.*//').tar.bz2 -C /build/buildd/
+sudo rm -rf /build/buildd/linux-$(uname -r | sed 's/-.*//')+sudo mv /build/buildd/linux-source-$(uname -r | sed 's/-.*//') /build/buildd/linux-$(uname -r | sed 's/-.*//')
+}}} +请 *注意* 当内核更新的时候这一步 *安装内核源码* 需要再做一次。 + +==== Fedora ==== +===== 安装Linux内核调试镜像 ===== +使用下面的命令: +{{{ +sudo debuginfo-install kernel +}}} +或者: +{{{ +sudo yum --enablerepo=fedora-debuginfo install kernel-debuginfo +}}}+于是你可以在"/usr/lib/debug/lib/modules/$(uname -r)/vmlinux"找到内核调试镜 像。
+ +===== 安装Linux内核开发包 ===== +{{{ +sudo yum install kernel-devel-$(uname -r) +}}} +请 *注意* 在升级过内核包之后,你可能需要重新调用这个命令。 + +==== 其他系统 ==== +需要安装Linux内核调试镜像和Linux内核源码包。 + +=== 确定Linux内核调试镜像是正确的 ===+因为GDB从Linux内核调试镜像里取得地址信息和调试信息,所以使用正确的Linux内核 调试镜像是非常重要的。所以在使用KGTP前,请先做检查。<br>
+有2个方法进行检查,我建议2个方法都做一次来确保Linux内核调试镜像是正确的。 +<br> +<br>+请 *注意* 如果你确定使用了正确的Linux内核调试镜像但是不能通过这个两个方法。 请看 [http://code.google.com/p/kgtp/wiki/HOWTOCN#处理Linux内核调试镜像地址信 息和Linux内核执行时] 。
+ +==== 当前Linux内核调试镜像在哪 ==== +在UBUNTU中,你可以在"/usr/lib/debug/boot/vmlinux-$(uname -r)"找到它。<br>+在Fedora中,你可以在"/usr/lib/debug/lib/modules/$(uname -r)/vmlinux"找到 它。<br>
+如果你自己编译的内核,你可以在内核编译目录找到vmlinux文件。 + +==== 使用/proc/kallsyms ==== +在运行着要trace的内核的系统上,用下面的命令取得sys_read和sys_write的地址: +{{{ +sudo cat /proc/kallsyms | grep sys_read +ffffffff8117a520 T sys_read +sudo cat /proc/kallsyms | grep sys_write +ffffffff8117a5b0 T sys_write +}}}+于是我们就可以得到sys_read的地址是0xffffffff8117a520,sys_write的地址是 0xffffffff8117a5b0。<br>
+之后我们用GDB从Linux内核调试镜像中取得sys_read和sys_write的地址: +{{{ +gdb ./vmlinux +(gdb) p sys_read+$1 = {long int (unsigned int, char *, size_t)} 0xffffffff8117a520 <sys_read>
+(gdb) p sys_write+$2 = {long int (unsigned int, const char *, size_t)} 0xffffffff8117a5b0 <sys_write>
+}}} +sys_read和sys_write的地址一样,所以Linux内核调试镜像是正确的。 + +==== 使用linux_banner ==== +{{{ +sudo gdb ./vmlinux +(gdb) p linux_banner+$1 = "Linux version 3.4.0-rc4+ (teawater@teawater-Precision-M4600) (gcc version 4.6.3 (GCC) ) #3 SMP Tue Apr 24 13:29:05 CST 2012\n"
+}}} +linux_banner是Linux内核调试镜像里的内核信息。<br>+之后,根据 [http://code.google.com/p/kgtp/wiki/HOWTOCN#让GDB连接到KGTP] 里 的方法连接到KGTP上并再次打印linux_banner。
+{{{ +(gdb) target remote /sys/kernel/debug/gtp +Remote debugging using /sys/kernel/debug/gtp +0x0000000000000000 in irq_stack_union () +(gdb) p linux_banner+$2 = "Linux version 3.4.0-rc4+ (teawater@teawater-Precision-M4600) (gcc version 4.6.3 (GCC) ) #3 SMP Tue Apr 24 13:29:05 CST 2012\n"
+}}}+这个linux_banner是KGTP正在trace的内核的内核信息,如果相同,则Linux内核调试 镜像是正确的。
+ +=== 处理Linux内核调试镜像地址信息和Linux内核执行时不同的问题 ===+在X86_32上,用 [http://code.google.com/p/kgtp/wiki/HOWTOCN#确定Linux内核调 试镜像是正确的] 介绍的方法发现Linux内核调试镜像地址信息和Linux内核执行时不 同,而且确定使用的Linux内核调试镜像是正确的。<br>
+这个问题是因为: +{{{ +Processor type and features ---> + (0x1000000) Physical address where the kernel is loaded + (0x100000) Alignment value to which kernel should be aligned +}}}+这个两个参数的值不同。请 *注意* "Physical address where the kernel is loaded" 有时不会在配置的时候显示,你可以通过搜索 "PHYSICAL_START" 取得它的 值。<br>
+<br>+你可以通过修改 "Alignment value to which kernel should be aligned" 的值 和 "Physical address where the kernel is loaded" 来处理这个问题。<br>
+这个问题不影响X86_64。 + +== 取得KGTP == +=== 通过http下载KGTP === +请到 [https://github.com/teawater/kgtp] 或者 [UPDATE] 去下载源码包。<br> +或者你可以访问镜像: +[https://code.csdn.net/teawater/kgtp/tree/master]<br> +[https://git.oschina.net/teawater/kgtp]<br> +[https://www.gitshell.com/teawater/kgtp/]<br> + +=== 通过git下载KGTP === +下面的命令将让你取得KGTP的最新版本: +{{{ +git clone https://github.com/teawater/kgtp.git +}}} +<br> +下面的命令将让你取得KGTP最后的发布版本: +{{{ +https://github.com/teawater/kgtp.git +git checkout release -b release +}}} + +=== 镜像 === +[https://code.csdn.net/teawater/kgtp]<br> +[https://git.oschina.net/teawater/kgtp]<br> +[https://www.gitshell.com/teawater/kgtp/]<br> + +== 配置KGTP ==+下面这部分是在KGTP Makefile里的配置。用这个配置,KGTP将自动和当前系统的内核 一起编译。
+{{{ +KERNELDIR := /lib/modules/`uname -r`/build +CROSS_COMPILE := +}}}+KERELDIR 设置了你要一起编译的内核,默认情况下,KGTP会和当前的内核一起编译。 <br> +请 *注意* 这个目录应该是内核编译目录或者linux-headers目录,而不是内核源码目 录。内核编译目录只有在编译成功后才能使用。<br>
+CROSS_COMPILE 设置编译KGTP的编译器前缀名。留空则使用默认编译器。<br> +ARCH 是体系结构。<br> +<br> ++或者你可以通过修改KGTP目录里的Makefile选择你要和哪个内核一起编译以及你用什 么编译器编译KGTP。<br>
+例如: +{{{ +KERNELDIR := /home/teawater/kernel/bamd64 +CROSS_COMPILE :=x86_64-glibc_std- +ARCH := x86_64 +}}} +KERNELDIR 设置为 /home/teawater/kernel/bamd64。 +Compiler 设置为 x86_64-glibc_std-gcc。 + +== 编译KGTP == +=== 普通编译 === +{{{ +cd kgtp/ +make +}}}+在一些编译环境中(例如Android)将出现一些编译应用程序getmod或者getframe的错 误。请忽略这些错误并使用目录中的gtp.ko。<br> +如果你在Fedora上得到出错信息"/usr/bin/ld: cannot find -lc",请用下面的命令 处理其。
+{{{ +sudo yum install glibc-static +}}} + +=== 用一些特殊选项编译KGTP === +大部分时候,KGTP可以自动选择正确的参数和和各种版本的Linux内核一起编译。<br> +但是如果你想配置一些特殊选项,可以按照下面的介绍来做:<br> + +用这个选项,KGTP将不自动选择任何编译选项。 +{{{ +make AUTO=0 +}}} + +用这个选项,KGTP将使用简单frame替代KGTP ring buffer。<br> +简单frame不支持gtpframe_pipe,它现在只用来调试KGTP。 +{{{ +make AUTO=0 FRAME_SIMPLE=1 +}}} + +用这个选项,$clock将返回rdtsc的值而不是local_clock。 +{{{ +make AUTO=0 CLOCK_CYCLE=1 +}}} + +用这个选项,KGTP可以用procfs替代debugfs。 +{{{ +make AUTO=0 USE_PROC=1 +}}} + +这些选线可以一起使用,例如: +{{{ +make AUTO=0 FRAME_SIMPLE=1 CLOCK_CYCLE=1 +}}} + +== 安装和卸载 KGTP ==+因为KGTP可以直接在编译目录里insmod,所以不编译后不安装也可以直接使用(见 [https://code.google.com/p/kgtp/wiki/HOWTOCN#如何让GDB连接KGTP])。但是如果 需要也可以将其安装到系统中。
+安装: +{{{ +cd kgtp/ +sudo make install +}}} +卸载: +{{{ +cd kgtp/ +sudo make uninstall +}}} + +== 和DKMS一起使用KGTP == +如果你需要的话,你还可以让DKMS来使用KGTP。<br> +下面的命令将拷贝KGTP的文件到DKMS需要的目录中。 +{{{ +cd kgtp/ +sudo make dkms +}}}+于是你可以用DKMS命令控制KGTP。请到 http://linux.dell.com/dkms/manpage.html 去看如何使用DKMS。
+ +== 使用KGTP Linux内核patch ==+大多数时候,你不需要KGTP patch,因为KGTP以一个LKM的形式编译安装更为方便。但 是为了帮助人们集成KGTP到他们自己的内核树,KGTP也提供了patch.
+在KGTP目录中:<br> + * *gtp_3.7_to_upstream.patch* 是给Linux kernel 从3.7到upstream的patch。 + * *gtp_3.0_to_3.6.patch* 是给Linux kernel 从3.0到3.6的patch。 + * *gtp_2.6.39.patch* 是给Linux kernel 2.6.39的patch。 + * *gtp_2.6.33_to_2.6.38.patch* 是给Linux kernel 从2.6.33到2.6.38的patch。 + * *gtp_2.6.20_to_2.6.32.patch* 是给Linux kernel 从2.6.20到2.6.32的patch。+ * *gtp_older_to_2.6.19.patch* 是给Linux kernel 2.6.19以及更早版本的 patch。
+ +== 安装可以和KGTP一起使用的GDB ==+早于7.6版本的GDB的tracepoint功能有一些bug,而且还有一些功能做的不是很好。 <br> +所以如果你的GDB小于7.6请到 [https://code.google.com/p/gdbt/] 去安装可以和 KGTP一起使用的GDB。这里提供UBUBTU, CentOS, Fedora, Mandriva, RHEL, SLE, openSUSE源。其他系统还可以下载静态编译版本。<br> +如果你有GDB的问题,请根据这里的信息 [http://code.google.com/p/kgtp/wiki/HOWTOCN#需要帮助或者汇报问题]取得帮助。
+ + += 如何让GDB连接KGTP = +要使用KGTP的功能需要先让GDB连接到KGTP上。 +== 普通Linux == +=== 安装KGTP模块 === +如果你已经安装了KGTP在你的系统中,你可以: +{{{ +sudo modprobe gtp +}}} +或者你可以直接使用KGTP目录里的文件: +{{{ +cd kgtp/ +sudo insmod gtp.ko +}}} +=== 处理找不到"/sys/kernel/debug/gtp"的问题 ===+如果你有这个问题,请先确定你的内核config打开了"Debug Filesystem"。 [http://code.google.com/p/kgtp/wiki/HOWTOCN#如果你的系统内核是自己编译的 ]<br>
+ +如果它以及被打开了,请用下面命令mount sysfs。 +{{{ +sudo mount -t sysfs none /sys/ +}}}+也许你可能会得到一些错误例如"sysfs is already mounted on /sys",请忽略他 们。
+<br> + +请用下面命令mount debugfs。 +{{{ +mount -t debugfs none /sys/kernel/debug/ +}}} +然后你就找到"/sys/kernel/debug/gtp"。 +=== 让GDB连接到KGTP ===+请 *注意* 让GDB打开正确的vmlinux文件非常重要。请到 [http://code.google.com/p/kgtp/wiki/HOWTOCN#确定Linux内核调试镜像是正确的] 去看下如何做。
+==== GDB在本地主机上 ==== +{{{ +sudo gdb ./vmlinux +(gdb) target remote /sys/kernel/debug/gtp +Remote debugging using /sys/kernel/debug/gtp +0x0000000000000000 in ?? () +}}} +然后你就可以用GDB命令调试和跟踪Linux内核了。 +==== 如果GDB在远程主机上 ==== +用nc把KGTP接口映射到端口1024上。 +{{{ +sudo su +nc -l 1234 </sys/kernel/debug/gtp >/sys/kernel/debug/gtp +#(nc -l -p 1234 </sys/kernel/debug/gtp >/sys/kernel/debug/gtp 给老版本的nc) +}}} +之后,nc会在那里等待连接。<br> +让GDB连接1234端口。 +{{{ +gdb-release ./vmlinux +(gdb) target remote xxx.xxx.xxx.xxx:1234 +}}} +然后你就可以用GDB命令调试和跟踪Linux内核了。 + +== Android ==+这个视频介绍了使用GDB连接Android上KGTP的过程,可访问 [http://www.tudou.com/programs/view/qCumSPhByFI/] 或者 [http://youtu.be/9YMpAvsl37I] 进行观看。
+=== 安装KGTP模块 === +*第一步* 确定ADB已经连接到Android上。<br> +*第二步* 拷贝KGTP模块到Android上。 +{{{ +sudo adb push gtp.ko / +}}}+目录 "/" 可能是只读的。你可以选择其他目录或者用命令"sudo adb shell mount -o rw,remount /"把这个目录remount为可写。<br>
+*第三步* 安装KGTP模块。 +{{{ +adb shell insmod /gtp.ko +}}} +=== 处理找不到"/sys/kernel/debug/gtp"的问题 ===+如果你有这个问题,请先确定你的内核config打开了"Debug Filesystem"。 [https://code.google.com/p/kgtp/wiki/HOWTOCN#如果是Android内核]<br>
+ +如果它以及被打开了,请用下面命令mount sysfs。 +{{{ +sudo adb shell mount -t sysfs none /sys/ +}}} +也许你可能会得到一些错误例如"Device or resource busy",请忽略他们。 +<br> + +请用下面命令mount debugfs。 +{{{ +sudo adb shell mount -t debugfs none /sys/kernel/debug/ +}}} +然后你就找到"/sys/kernel/debug/gtp"。 +=== GDB连接KGTP === +用nc将KGTP接口映射到1024端口上。 +{{{ +adb forward tcp:1234 tcp:1234 +adb shell "nc -l -p 1234 </sys/kernel/debug/gtp >/sys/kernel/debug/gtp"+#(adb shell "nc -l 1234 </sys/kernel/debug/gtp >/sys/kernel/debug/gtp" 给新 版本的nc)
+}}} +之后,nc会在那里等待连接。<br> +让GDB连接1234端口。 +{{{ +gdb-release ./vmlinux +(gdb) target remote :1234 +}}} +然后你就可以用GDB命令调试和跟踪Linux内核了。 + += 增加模块的符号信息到GDB = +有时你需要添加一个Linux内核模块的符号信息到GDB好调试其。<br>+手动增加符号信息不太容易,所以KGTP包里包含了GDB Python脚本"getmod.py"和程 序"getmod"可以帮到你。
+ +== 如何使用getmod == +"getmod" 是用C写的所以你可以把它用在任何地方即使是一个嵌入式环境。<br> +例如: +{{{ +#下面的命令将把Linux内核模块信息以GDB命令的格式保存到文件/tmp/mi。 +sudo getmod >/tmp/mi +#在GDB那边: +(gdb) source /tmp/mi+add symbol table from file "/lib/modules/2.6.39-rc5+/kernel/fs/nls/nls_iso8859-1.ko" at
+ .text_addr = 0xf80de000 + .note.gnu.build-id_addr = 0xf80de088 + .exit.text_addr = 0xf80de074 + .init.text_addr = 0xf8118000 + .rodata.str1.1_addr = 0xf80de0ac + .rodata_addr = 0xf80de0c0 + __mcount_loc_addr = 0xf80de9c0 + .data_addr = 0xf80de9e0 + .gnu.linkonce.this_module_addr = 0xf80dea00 +#这条GDB命令后,所有Linux内核模块信息都被装载进GDB了。+#After this GDB command, all the Linux Kernel module info is loaded into GDB.
+}}} +如果你使用远程调试或者离线调试,你可以需要修改基本目录。下面是一个例子: +{{{ +#/home/teawater/kernel/b26是GDB所在主机上内核模块所在的路径 +sudo ./getmod -r /home/teawater/kernel/b26 >~/tmp/mi +}}} + +== 如何使用getmod.py ==+请 *注意* [https://code.google.com/p/gdbt/]下载的静态编译GDB不能使用 getmod.py。<br>
+在使用getmod.py前连接到KGTP。 +{{{ +(gdb) source ~/kgtp/getmod.py +}}} +于是这个脚本将自动装载Linux内核模块到GDB中。 + += 如何使用GDB控制KGTP跟踪和调试Linux内核 = +== 在普通模式直接访问当前值 ==+在GDB连到KGTP上以后,如果没有用GDB命令"tfind"选择一条trace帧缓存里面的条 目,GDB就处于 *普通模式*。于是你可以直接访问内存(Linux内核或者用户程序)的值 和trace状态变量的值。<br> +如果你选择了一个trace帧条目,可以用GDB命令"tfind -1"返回到普通模式。请到 [http://code.google.com/p/kgtp/wiki/HOWTOCN#用tfind选择trace帧缓存里面的条目 ]取得GDB命令"tfind"的更多信息。
+ +=== Linux内核的内存 === +例如你可以用下面的命令访问"jiffies_64": +{{{ +(gdb) p jiffies_64 +}}} +或者你可以用下面的命令访问"static LIST_HEAD(modules)"的第一条记录: +{{{+(gdb) p *((struct module *)((char *)modules->next - ((size_t) &(((struct module *)0)->list))))
+}}} +或者你可以访问"DEFINE_PER_CPU(struct device `*`, mce_device);"CPU0的数据: +{{{ +p *(struct device *)(__per_cpu_offset[0]+(uint64_t)(&mce_device)) +}}} +如果想在用一个GDB命令显示多个变量,请使用下面的例子: +{{{+(gdb) printf "%4d %4d %4d %4d %4d %4d %18d %lu\n", this_rq->cpu, this_rq->nr_running, this_rq->nr_uninterruptible, nr_active, calc_load_tasks->counter, this_rq->calc_load_active, delta, this_rq->calc_load_update
+2 1 0 0 0 0 673538312 717077240 +}}} + +=== 用户程序的内存 === +KGTP可以不同停止应用层程序的情况下直接读取其内存,例如: +{{{ +#连接KGTP(这里和前面介绍的连接方法不同) +(gdb) target extended-remote /sys/kernel/debug/gtp +#增加一个新的inferior用来分析应用程序的信息。 +(gdb) add-inferior +Added inferior 2 +#切换到这个inferior +(gdb) inferior 2 +[Switching to inferior 2 [<null>] (<noexec>)] +#转载这个程序的符号文件 +(gdb) file ~/kernel/svn/bak/a.out +Reading symbols from /home/teawater/kernel/svn/bak/a.out...done. +#attach到这个进程上(这不会停止这个程序)。 +(gdb) attach 10039 +Attaching to program: /home/teawater/kernel/svn/bak/a.out, Remote target+Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...(no debugging symbols found)...done.
+Loaded symbols for /lib/x86_64-linux-gnu/libc.so.6+Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
+Loaded symbols for /lib64/ld-linux-x86-64.so.2 +0x0000000000000000 in ?? () +#于是可以读这个程序的内存 +(gdb) p c +$3 = -1222013392 +(gdb) p c +$4 = -590910451 +}}}+在这个例子中,我使用了一个多inferior命令。请到 [http://sourceware.org/gdb/current/onlinedocs/gdb/Inferiors-and-Programs.html] 去取得其更多相关信息。
+ +=== trace状态变量 === +你可以使用和访问内存一样的命令访问TSV。+请到 [http://code.google.com/p/kgtp/wiki/HOWTOCN#如果使用trace状态变量] 取 得更多TSV的信息。
+ +== GDB tracepoint ==+tracepoint就是GDB定义一些地址和一些动作。在tracepoint启动之后,当Linux内核 执行到那些地址的时候,KGTP将执行这些动作(它们中的有些会收集数据并存入 tracepoint帧缓冲)并把它们发给调试目标(KGTP)。而后,Linux内核将继续执行。<br> +KGTP提供了一些接口可以让GDB或者其他程序取出tracepoint帧缓冲的数据做分析。 <br> +关于这些接口,文档前面已经介绍了"/sys/kernel/debug/gtp",将在后面介 绍"/sys/kernel/debug/gtpframe" 和 "/sys/kernel/debug/gtpframe_pipe"。<br>
+<br>+GDB tracepoint文档在 [http://sourceware.org/gdb/current/onlinedocs/gdb/Tracepoints.html]。
+ +=== 设置tracepint ===+trace命令非常类似break命令,它的参数可以是文件行,函数名或者一个地址。 trace将定义一个或者多个地址定义一个tracepoint,KGTP将在这个点做一些动作。 <br>
+ +这是一些使用trace命令的例子: +{{{ +(gdb) trace foo.c:121 // 一个文件和行号 + +(gdb) trace +2 // 2行以后 + +(gdb) trace my_function // 函数的第一行 + +(gdb) trace *my_function // 函数的第一个地址 + +(gdb) trace *0x2117c4 // 一个地址 +}}} + +==== 这个函数确实存在但是设置tracepoint到上面会失败如何处理 ====+GCC为了提高程序执行效率会inline一些static函数。因为目标文件没有inline函数的 符号,所以你不能设置tracepoint在函数名上。<br>
+你可以用"trace 文件:行号"在其上设置断点。 + +=== 如何设置条件tracepoint === +[http://sourceware.org/gdb/current/onlinedocs/gdb/Tracepoint-Conditions.html]<br>+和breakpoint一样,我们可以设置tracepoint的触发条件。而且因为条件检查是在 KGTP执行的,所以速度比breakpoint的条件检查快很多。<br>
+例如: +{{{ +(gdb) trace handle_irq if (irq == 47) +}}} +tracepoint 1的动作将只在irq是47的时候才被执行。<br><br> ++你还可以用GDB命令"condition"设置tracepoint的触发条件。GDB命令"condition N COND"将设置tracepoint N只有条件COND为真的时候执行。<br>
+例如: +{{{ +(gdb) trace handle_irq +(gdb) condition 1 (irq == 47) +}}} +GDB命令"info tracepoint"将显示tracepoint的ID。<br><br> ++$bpnum的值是最后一个GDB tracepoint的ID,所以你可以不取得tracepoint的ID就用 condition来设置最后设置的tracepoint的条件,例如:
+{{{ +(gdb) trace handle_irq +(gdb) condition $bpnum (irq == 47) +}}} + +==== 如何处理错误 "Unsupported operator (null) (52) in expression." ====+如果你使用关于字符串的条件tracepoint,你在调用"tstart"的时候可能得到这个出 错信息。<br>
+你可以转化char为int来处理这个问题,例如: +{{{ +(gdb) p/x 'A' +$4 = 0x41 +(gdb) condition 1 (buf[0] == 0x41) +}}} + +=== `actions [num]` ===+这个命令将设置一组action当tracepoint num触发的时候执行。如果没有设置num则将 设置action到最近创建的tracepoint上(因此你可以定义一个tracepoint然后直接输入 actions而不需要参数)。然后就要在后面输入action,最后以end为结束。到目前为 止,支持的action有collect,teval和while-stepping。
+ +==== `collect expr1, expr2, ...` ====+当tracepoint触发的时候,收集表达式的值。这个命令可接受用逗号分割的一组列 表,这些列表除了可以是全局,局部或者本地变量,还可以是下面的这些参数:
+{{{ +$regs 收集全部寄存器。 +$args 收集函数参数。 +$locals 收集全部局部变量。 +}}}+请 *注意* collect 一个指针(`collect ptr`)将只能collect这个指针的地址. 在指 针前面增加一个 `*` 将会让action collect指针指向的数据(`collect *ptr`)。
+ + +==== `teval expr1, expr2, ...` ====+当tracepoint触发的时候,执行指定的表达式。这个命令可接受用逗号分割的一组列 表。表达式的结果将被删除,所以最主要的作用是把值设置到trace状态变量中 (see [http://code.google.com/p/kgtp/wiki/HOWTOCN#普通trace状态变量]),而不用想 collect一样把这些值存到trace帧中。
+ +==== while-stepping n ====+请到 [https://code.google.com/p/kgtp/wiki/HOWTOCN#使用while-stepping让 Linux内核做单步] 去看如何使用它。
+ +=== 启动和停止 tracepoint === +tracepoint只有在用下面的GDB命令启动后才可以执行action: +{{{ +(gdb) tstart +}}} +它可以用下面的命令停止: +{{{ +(gdb) tstop +}}} + +=== Enable 和 disable tracepoint ===+和breakpoint一样,tracepoint可以使用GDB命令 "enable" 和 "disable"。但是请 *注意* 它们只在tracepoint停止的时候有效。
+ +=== 用tfind选择trace帧缓存里面的条目 === +tracepoint停止的时候,GDB命令"tfind"可以用来选择trace帧缓存里面的条目。<br>+当GDB在"tfind"模式的时候,其只能显示tracepoint action collect的存在于这个条 目中的数据。所以GDB将输出一些错误信息如果想打印没有collect的数据例如函数的参 数。这不是bug,不用担心。<br> +如果想选择下一个条目,可以再次使用命令"tfind"。还可以用"tfind 条目ID"去选择 某个条目。<br> +要回到普通模式([http://code.google.com/p/kgtp/wiki/HOWTOCN#在普通模式直接访 问当前值]),请使用GDB命令"tfind -1"。 +请到 [http://sourceware.org/gdb/current/onlinedocs/gdb/tfind.html] 取得它的 详细信息。
++==== 如何处理错误 "No such file or directory." 或者 "没有那个文件或目录." ====
+当GDB不能找到Linux内核源码的时候,其就会显示这个错误信息。 +例如: +{{{ +(gdb) tfind +Found trace frame 0, tracepoint 1+#0 vfs_read (file=0xffff8801b6c3a500, buf=0x3f588b8 <Address 0x3f588b8 out of bounds>, count=8192, + pos=0xffff8801eee49f48) at /build/buildd/linux-3.2.0/fs/read_write.c:365
+365 /build/buildd/linux-3.2.0/fs/read_write.c: 没有那个文件或目录. +}}}+你可以用GDB命令 "set substitute-path" 处理它。前面这个例子Linux内核源码 在"/build/buildd/test/linux-3.2.0/"但是vmlinux让 GDB在"/build/buildd/linux-3.2.0/"找内核远啊,你可以处理他们:
+{{{+(gdb) set substitute-path /build/buildd/linux-3.2.0/ /build/buildd/test/linux-3.2.0/
+(gdb) tfind +Found trace frame 1, tracepoint 1+#0 vfs_read (file=0xffff8801c36e6400, buf=0x7fff51a8f110 <Address 0x7fff51a8f110 out of bounds>, count=16, + pos=0xffff8801761dff48) at /build/buildd/linux-3.2.0/fs/read_write.c:365
+365 { +}}}+GDB还提供其他的命令处理源码问题,请到 [http://sourceware.org/gdb/current/onlinedocs/gdb/Source-Path.html]取得他们 的介绍。
+ +=== 保存trace帧信息到一个文件中 ===+/sys/kernel/debug/gtpframe是一个当KGTP停止时的tfine格式(GDB可以读取它)的 接口。<br>
++请 *注意* 有些"cp"不能很好的处理这个问题,可以用"cat /sys/kernel/debug/gtpframe > ./gtpframe"拷贝它。<br>
+你可以在需要的时候打开文件gtpframe: +{{{ +(gdb) target tfile ./gtpframe+Tracepoint 1 at 0xffffffff8114f3dc: file /home/teawater/kernel/linux-2.6/fs/readdir.c, line 24.
+Created tracepoint 1 for target's tracepoint 1 at 0xffffffff8114f3c0. +(gdb) tfind +Found trace frame 0, tracepoint 1+#0 vfs_readdir (file=0xffff880036e8f300, filler=0xffffffff8114f240 <filldir>, buf=0xffff880001e5bf38)
+ at /home/teawater/kernel/linux-2.6/fs/readdir.c:24 +24 { +}}} + +=== 显示和存储tracepoint === +你可以用GDB命令"info tracepoints"显示所有的tracepoint。<br>+你可以用GDB命令"save tracepoints filename"保存所有的设置tracepoint的命令到 文件filename里。于是你可以在之后用GDB命令"source filename"设置重新这些 tracepoint。
+ +=== 删除tracepoint ===+GDB命令"delete id"将删除tracepoint id。如果"delete"没有参数,则删除所有 tracepoint。
+ +=== 用tracepoint从内核中某点取得寄存器信息 === +下面是记录内核调用函数"vfs_readdir"时的寄存器信息的例子: +{{{ +(gdb) target remote /sys/kernel/debug/gtp +(gdb) trace vfs_readdir +Tracepoint 1 at 0xc01a1ac0: file +/home/teawater/kernel/linux-2.6/fs/readdir.c, line 23. +(gdb) actions +Enter actions for tracepoint 1, one per line. +End with a line saying just "end". +>collect $reg +>end +(gdb) tstart +(gdb) shell ls +(gdb) tstop +(gdb) tfind +Found trace frame 0, tracepoint 1+#0 0xc01a1ac1 in vfs_readdir (file=0xc5528d00, filler=0xc01a1900 <filldir64>,
+ buf=0xc0d09f90) at /home/teawater/kernel/linux-2.6/fs/readdir.c:23+23 /home/teawater/kernel/linux-2.6/fs/readdir.c: No such file or directory.
+ in /home/teawater/kernel/linux-2.6/fs/readdir.c +(gdb) info reg +eax 0xc5528d00 -984445696 +ecx 0xc0d09f90 -1060069488 +edx 0xc01a1900 -1072031488 +ebx 0xfffffff7 -9 +esp 0xc0d09f8c 0xc0d09f8c +ebp 0x0 0x0 +esi 0x8061480 134616192 +edi 0xc5528d00 -984445696 +eip 0xc01a1ac1 0xc01a1ac1 <vfs_readdir+1> +eflags 0x286 [ PF SF IF ] +cs 0x60 96 +ss 0x8061480 134616192 +ds 0x7b 123 +es 0x7b 123 +fs 0x0 0 +gs 0x0 0 +(gdb) tfind +Found trace frame 1, tracepoint 1 +0xc01a1ac1 23 in /home/teawater/kernel/linux-2.6/fs/readdir.c +(gdb) info reg +eax 0xc5528d00 -984445696 +ecx 0xc0d09f90 -1060069488 +edx 0xc01a1900 -1072031488 +ebx 0xfffffff7 -9 +esp 0xc0d09f8c 0xc0d09f8c +ebp 0x0 0x0 +esi 0x8061480 134616192 +edi 0xc5528d00 -984445696 +eip 0xc01a1ac1 0xc01a1ac1 <vfs_readdir+1> +eflags 0x286 [ PF SF IF ] +cs 0x60 96 +ss 0x8061480 134616192 +ds 0x7b 123 +es 0x7b 123 +fs 0x0 0 +gs 0x0 0 +}}} + +=== 用tracepoint从内核中某点取得变量的值 === +下面是记录内核调用函数"vfs_readdir"时"jiffies_64"的值的例子: +{{{ +(gdb) target remote /sys/kernel/debug/gtp +(gdb) trace vfs_readdir+Tracepoint 1 at 0xc01ed740: file /home/teawater/kernel/linux-2.6/fs/readdir.c, line 24.
+(gdb) actions +Enter actions for tracepoint 1, one per line. +End with a line saying just "end". +>collect jiffies_64 +>collect file->f_path.dentry->d_iname +>end +(gdb) tstart +(gdb) shell ls+arch drivers include kernel mm Module.symvers security System.map virt +block firmware init lib modules.builtin net sound t vmlinux +crypto fs ipc Makefile modules.order scripts source usr vmlinux.o
+(gdb) tstop +(gdb) tfind +Found trace frame 0, tracepoint 1+#0 0xc01ed741 in vfs_readdir (file=0xf4063000, filler=0xc01ed580 <filldir64>, buf=0xd6dfdf90)
+ at /home/teawater/kernel/linux-2.6/fs/readdir.c:24 +24 { +(gdb) p jiffies_64 +$1 = 4297248706 +(gdb) p file->f_path.dentry->d_iname +$1 = "b26", '\000' <repeats 28 times> +}}} + +=== 显示当前这一条trace缓存里存储的所有信息 === +在用"tfind"选择好一个条目后,你可以用"tdump"。 +{{{ +(gdb) tdump +Data collected at tracepoint 1, trace frame 0: +$cr = void+file->f_path.dentry->d_iname = "gtp\000.google.chrome.g05ZYO\000\235\337\000\000\000\000\200\067k\364\200\067", <incomplete sequence \364>
+jiffies_64 = 4319751455 +}}} + +=== 取得 tracepoint 的状态 === +请用GDB命令"tstatus"。 + +=== 设置trace缓存为循环缓存 === +[http://sourceware.org/gdb/current/onlinedocs/gdb/Starting-and-Stopping-Trace-Experiments.html]<br> +帧缓存默认情况下不是循环缓存。当缓存满了的时候,tracepoint将停止。<br>+下面的命令将设置trace缓存为循环缓存,当缓存满了的时候,其将自动删除最早的数 据并继续trace。
+{{{ +(gdb) set circular-trace-buffer on +}}} + +=== GDB断开的时候不要停止tracepoint === +[http://sourceware.org/gdb/current/onlinedocs/gdb/Starting-and-Stopping-Trace-Experiments.html]<br> +默认情况下,当GDB断开KGTP的时候将自动停止tracepoint并删除trace帧。<br>+下面的命令将打开KGTP disconnect-trace。在设置之后,当GDB断开KGTP的时 候,KGTP将不停止tracepoint。GDB重新连到KGTP的时候,其可以继续控制KGTP。
+{{{ +(gdb) set disconnected-tracing on +}}} + +=== kprobes-optimization和tracepoint的执行速度 ===+因为tracepoint是和Linux内核一起执行,所以它的速度将影响到系统执行的速度。 <br> +KGTP tracepoint是基于Linux内核kprobe。因为普通kprobe是基于断点指令,所以它 的速度不是很快。<br><br>
++但是如果你的arch是X86_64 或者 X86_32 而且内核配置没有打开"Preemptible Kernel" (PREEMPT),kprobe的速度将被kprobes-optimization (CONFIG_OPTPROBES)提 高很多。<br>
+可以用下面的命令来确认: +{{{ +sysctl -A | grep kprobe +debug.kprobes-optimization = 1 +}}} +这个的意思就是你的系统支持kprobes-optimization。<br>+请 *注意* 一些KGTP的功能会导致tracepoint只能使用普通kprobe即使系统支持 kprobes-optimization。文档将在介绍这些功能的时候增加提醒,如果你很介意 tracepoint的速度就请避免使用这些功能。
+ +== 如何使用trace状态变量 == +[http://sourceware.org/gdb/current/onlinedocs/gdb/Trace-State-Variables.html]<br> +trace状态变量简称TSV。<br>+TSV可以在tracepoint action和condition中被访问,并且可以直接被GDB命令访问。 <br> +请 *注意* GDB 7.2.1和更晚的版本直接访问trace状态变量,比他们老的GDB只能通过 命令"info tvariables"取得trace状态变量的值。
+=== 普通trace状态变量 === +定义trace状态变量$c. +{{{ +(gdb) tvariable $c +}}} +trace状态变量 $c 被创建并初始化0。 +下面的action将使用$c记录内核里发生了多少次IRQ。 +{{{ +(gdb) target remote /sys/kernel/debug/gtp +(gdb) trace handle_irq +(gdb) actions +Enter actions for tracepoint 3, one per line. +End with a line saying just "end". +>collect $c #Save current value of $c to the trace frame buffer. +>teval $c=$c+1 #Increase the $c. +>end +}}} +你还可以将某个变量的值传到状态变量里,但是别忘记转化这个值为"uint64_t"。 +{{{ +>teval $c=(uint64_t)a +}}} +你可以取得$c的值当trace在运行或者停止的时候。 +{{{ +(gdb) tstart +(gdb) info tvariables +$c 0 31554 +(gdb) p $c +$5 = 33652 +(gdb) tstop +(gdb) p $c +$9 = 105559 +}}}+当使用tfind的时候,你可以分析trace frame buffer。如果trace状态变量被收集 了,你可以把它取出来。
+{{{ +(gdb) tstop +(gdb) tfind +(gdb) info tvariables +$c 0 0 +(gdb) p $c +$6 = 0 +(gdb) tfind 100 +(gdb) p $c +$7 = 100 +}}}+如果需要的时候,访问trace状态变量的tracepoint action将自动加锁,所以其可以 很好的处理trace状态变量的竞态条件问题。<br>
+下面这个例子即使在一个多CPU的环境也可以正常使用。 +{{{ +>teval $c=$c+1 +}}} + +=== Per_cpu trace状态变量 === +Per_cpu trace状态变量是一种特殊的普通trace状态变量。+当一个tracepoint action访问到其的时候,其将自动访问这个CPU的Per_cpu trace状 态变量。<br>
+ +它有两个优点:<br>+1. 访问Per_cpu trace状态变量的tracepoint actions不存在竞态条件问题,所以其 不需要对trace状态变量加锁。所以其在多核的机器上速度更快。
+<br> +2. 写针对记录某个CPU的tracepoint actions比普通trace状态变量更容易。 + +==== 如何定义 ==== +Per_cpu trace状态变量有两种类型: + +===== 本地CPU变量 ===== +{{{ +"per_cpu_"+string +}}} +或者 +{{{ +"p_"+string +}}} +例如: +{{{ +(gdb) tvariable $p_count +}}}+在tracepoint action中访问这个trace状态变量的时候,其将返回这个变量在这个 action运行的CPU上的值。
+===== CPU id变量 ===== +{{{ +"per_cpu_"+string+CPU_id +}}} +或者 +{{{ +"p_"+string+CPU_id +}}} +例如: +{{{ +(gdb) tvariable $p_count0 +(gdb) tvariable $p_count1 +(gdb) tvariable $p_count2 +(gdb) tvariable $p_count3 +}}}+在tracepoint action或者GDB命令行中访问这个变量的时候,其将返回这个变量在 CPU CPI_id 上的值。
+<br>+下面这个例子可以自动这个这台主机上的每个CPU定义CPU id变量。(请 *注意* 用这 些命令之前需要让GDB连上KGTP。)
+{{{ +(gdb) set $tmp=0 +(gdb) while $tmp<$cpu_number + >eval "tvariable $p_count%d",$tmp + >set $tmp=$tmp+1 + >end +}}} + +==== 例子1 ==== +这个例子定义了一个记录每个CPU调用多少次vfs_read的tracepoint。 +{{{ +tvariable $p_count +set $tmp=0 +while $tmp<$cpu_number + eval "tvariable $p_count%d",$tmp + set $tmp=$tmp+1 + end +trace vfs_read +actions + teval $p_count=$p_count+1 + end +}}} +于是你可以在"tstart"后显示每个CPU调用了多少次vfs_read: +{{{ +(gdb) p $p_count0 +$3 = 44802 +(gdb) p $p_count1 +$4 = 55272 +(gdb) p $p_count2 +$5 = 102085 +(gdb) p $p_count3 +}}} + +==== 例子2 ==== +这个例子记录了每个CPU上关闭IRQ时间最长的函数的stack dump。 +{{{ +set pagination off + +tvariable $bt=1024 +tvariable $p_count +tvariable $p_cc +set $tmp=0 +while $tmp<$cpu_number +eval "tvariable $p_cc%d",$tmp +set $tmp=$tmp+1 +end + +tvariable $ignore_error=1 + +trace arch_local_irq_disable + commands + teval $p_count=$clock + end +trace arch_local_irq_enable if ($p_count && $p_cc < $clock - $p_count) + commands + teval $p_cc = $clock - $p_count + collect $bt + collect $p_cc + teval $p_count=0 + end + +enable +set pagination on +}}} ++=== 特殊trace状态变量 $current_task,$current_task_pid,$current_thread_info,$cpu_id,$dump_stack,$printk_level,$printk_format,$printk_tmp,$clock,$hardirq_count,$softirq_count 和 $irq_count === +KGTP特殊trace状态变量$current_task,$current_thread_info,$cpu_id 和 $clock可以很容易的访问各种特殊的值,当你用GDB连到KGTP后就可以访问到他们。
+你可以在tracepoint条件和actions里使用他们。<br>+在tracepoint条件和actions里访问$current_task可以取得get_current()的返回值。 <br> +在tracepoint条件和actions里访问$current_task_pid可以取得 get_current()->pid的值。<br> +在tracepoint条件和actions里访问$current_thread_info可以取得 current_thread_info()的返回值。<br> +在tracepoint条件和actions里访问$cpu_id可以取得smp_processor_id()的返回值。 <br> +在tracepoint条件和actions里访问$clock可以取得local_clock()的返回值,也就是 取得纳秒为单位的时间戳。<br> +$rdtsc只在体系结构是X86或者X86_64的时候访问的到,任何时候访问它可以取得用指 令RDTSC取得的TSC的值。<br> +在tracepoint条件和actions里访问$hardirq_count可以取得hardirq_count()的返回 值。<br> +在tracepoint条件和actions里访问$softirq_count可以取得softirq_count()的返回 值。<br>
+在tracepoint条件和actions里访问$irq_count可以取得irq_count()的返回值。<br> + ***The diff for this file has been truncated for email.***