对于内核代码来说,跟踪局部变量是不可能的,因为没有检测任意内核指令的机制。即使在用户态,跟踪局部变量也有些复杂,因此,对于您给出的具体示例,跟踪返回值会更有意义some_fun()
反而。
如果必须跟踪任意局部变量,那么您将需要确定其在特定感兴趣点的位置(通常是寄存器或内存中的位置)。对于简单的情况,您可以通过反汇编函数并检查输出来完成此操作。对于更复杂的情况,使用 DWARF 构建对象然后找到DW_AT_location
局部变量 DIE 的属性。
找到变量的位置后,需要用 D 来表达它;请注意,寄存器是通过公开的uregs[]
大批。此外,您需要使用函数内的偏移量来描述探针,因为dtrace(1)
无法理解行号。请参阅《用户进程跟踪》中的“用户进程跟踪”部分Oracle Solaris 动态
追踪指南 https://docs.oracle.com/cd/E53394_01/html/E53395/index.html了解更多。
作为一个例子,我写了一个简单的程序,其中包含
int
foo(int i)
{
int x;
...
for (x = 0; x < 10; x++)
i += 2;
并使用 DWARF 将其构建为 amd64 可执行文件...
cc -m64 -g -o demo demo.c
...在寻找之前foo()
及其定义x
在输出中
的dwarfdump demo
:
< 1><0x000000e4> DW_TAG_subprogram
DW_AT_name "foo"
...
DW_AT_frame_base DW_OP_reg6
< 2><0x00000121> DW_TAG_variable
DW_AT_name "x"
...
DW_AT_location DW_OP_fbreg -24
x
被描述为DW_OP_fbreg -24
but DW_OP_fbreg
其本身一定是
替换为父函数的结果DW_AT_frame_base
属性,即DW_OP_reg6
。 DWARF 使用自己的与架构无关的
寄存器的编号以及到各个寄存器的映射最多可达
适当的标准机构。在这种情况下,AMD64 ABI https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf告诉
DWARF 寄存器 6 对应于我们%rbp
. Thus x
存储在%rbp - 0x18
。 (有关 DWARF 本身的更多信息,我推荐 Michael Eager 的DWARF 调试格式简介 http://dwarfstd.org/doc/Debugging%20using%20DWARF-2012.pdf.)
因此,如果您发现您所在的源代码行
感兴趣的是偏移量 0x32(也许通过检查 DWARF
行表),那么你可以编写一个如下的探针:
pid$target:a.out:foo:32
{
self->up = (uintptr_t)(uregs[R_RBP] - 0x18);
self->kp = (int *)copyin(self->up, sizeof (int));
printf("x = %d\n", *self->kp);
self->up = 0;
self->kp = 0;
}
这是我运行演示程序时看到的:
# dtrace -q -s test.d -c /tmp/demo
x = 1
x = 2
x = 3
x = 4
x = 5
x = 6
x = 7
x = 8
x = 9
x = 10
#