Dtrace 中的局部变量
Local Variables in Dtrace
如何使用 dtrace 访问函数的局部变量?
例如,在下面的代码片段中,我想使用 dtrace 了解变量 x 的值。
void foo(int a){
int x=some_fun(a);
}
内核代码不可能跟踪局部变量,因为没有检测任意内核指令的机制。即使在用户领域,跟踪局部变量也有些复杂,因此,对于您给出的具体示例,跟踪 some_fun()
的 return 值更有意义。
如果您必须跟踪任意局部变量,那么您需要确定其在特定兴趣点的位置(通常是寄存器或内存中的位置)。对于简单的情况,您可以通过反汇编函数并检查输出来做到这一点。对于更复杂的情况,使用 DWARF 构建对象然后找到局部变量的 DIE 的 DW_AT_location
属性可能会有所帮助。
找到变量的位置后,需要用 D 表示;请注意,寄存器是通过 uregs[]
数组公开的。此外,您需要使用函数内的偏移量来描述您的探针,因为 dtrace(1)
无法理解行号。请参阅 Oracle Solaris Dynamic 中有关 "User Process Tracing" 的部分
追踪指南 更多。
举个例子,我写了一个简单的程序,其中包含
int
foo(int i)
{
int x;
...
for (x = 0; x < 10; x++)
i += 2;
并构建它,作为 amd64 executable,使用 DWARF...
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
但 DW_OP_fbreg
本身必须是
由父函数的 DW_AT_frame_base
的结果替换
属性,即 DW_OP_reg6
。 DWARF 使用自己的架构不可知论
寄存器的编号和到各个寄存器的映射取决于
适当的标准机构。在这种情况下,AMD64 ABI 告诉
我们知道 DWARF 寄存器 6 对应于 %rbp
。因此 x
存储在
%rbp - 0x18
。 (有关 DWARF 本身的更多信息,我推荐 Michael Eager 的
Introduction to the DWARF Debugging Format.)
因此,如果您发现您所在的源行
感兴趣的是偏移量 0x32(可能通过检查 DWARF
line table) 然后你可以写一个像这样的探测:
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
#
如何使用 dtrace 访问函数的局部变量?
例如,在下面的代码片段中,我想使用 dtrace 了解变量 x 的值。
void foo(int a){
int x=some_fun(a);
}
内核代码不可能跟踪局部变量,因为没有检测任意内核指令的机制。即使在用户领域,跟踪局部变量也有些复杂,因此,对于您给出的具体示例,跟踪 some_fun()
的 return 值更有意义。
如果您必须跟踪任意局部变量,那么您需要确定其在特定兴趣点的位置(通常是寄存器或内存中的位置)。对于简单的情况,您可以通过反汇编函数并检查输出来做到这一点。对于更复杂的情况,使用 DWARF 构建对象然后找到局部变量的 DIE 的 DW_AT_location
属性可能会有所帮助。
找到变量的位置后,需要用 D 表示;请注意,寄存器是通过 uregs[]
数组公开的。此外,您需要使用函数内的偏移量来描述您的探针,因为 dtrace(1)
无法理解行号。请参阅 Oracle Solaris Dynamic 中有关 "User Process Tracing" 的部分
追踪指南 更多。
举个例子,我写了一个简单的程序,其中包含
int
foo(int i)
{
int x;
...
for (x = 0; x < 10; x++)
i += 2;
并构建它,作为 amd64 executable,使用 DWARF...
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
但 DW_OP_fbreg
本身必须是
由父函数的 DW_AT_frame_base
的结果替换
属性,即 DW_OP_reg6
。 DWARF 使用自己的架构不可知论
寄存器的编号和到各个寄存器的映射取决于
适当的标准机构。在这种情况下,AMD64 ABI 告诉
我们知道 DWARF 寄存器 6 对应于 %rbp
。因此 x
存储在
%rbp - 0x18
。 (有关 DWARF 本身的更多信息,我推荐 Michael Eager 的
Introduction to the DWARF Debugging Format.)
因此,如果您发现您所在的源行 感兴趣的是偏移量 0x32(可能通过检查 DWARF line table) 然后你可以写一个像这样的探测:
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
#