[lttng-dev] [RFC] Dynamic instrumentation support in UST

Zifei Tong soariez at gmail.com
Fri Jul 26 11:40:33 EDT 2013

Hi everyone,

Here is the RFC for the dynamic instrumentation support in UST.

It contains a command line interface proposal and some notes on
implementation details.

Please provide your comments. Any questions, or fixes are VERY welcome.

Command Line Interface

The proposed command line interface is very similar to the current lttng kernel
dynamic probe interface [1].

To enable a dynamic probe in a running process at given address, you can use:

    lttng enable-event NAME -u --pid PID
        --probe (addr | symbol | symbol+offset)
                               Dynamic UST probe.
                               Addr and offset can be octal (0NNN...),
                               decimal (NNN...) or hexadecimal (0xNNN...)

This will place a bare probe at certain address.


    # lttng enable-event aname -u --pid 8964 --probe foo+0x6
    # lttng enable-event aname -u --pid 8964 --probe 0xdeadbeef

You can also enable a tracepoint at the entry of a given function:

    lttng enable-event NAME -u --pid PID
        --function (addr | symbol | symbol+offset)
                             Dynamic UST function entry probe.
                             Addr and offset can be octal (0NNN...),
                             decimal (NNN...) or hexadecimal (0xNNN...)

This will place a bare probe at the entry point of certain function.


    # lttng enable-event aname -u --pid 8964 --function foo

Being only able to attach and instrument existing processes is sometimes
restricted. If we can have #15 [2] implemented, then we can extend the command
to support dynamic instrumentation.

    lttng trace -c COMMAND
        --probe (addr | symbol | symbol+offset)
        --function (addr | symbol | symbol+offset)

This will execute given program and place probes at certain places.


    # lttng trace -c "ls -l" --probe main+0x10
    # lttng trace -c "ls -l" --function main

Probes collecting context data are more useful than bare ones. We can extend
the add-context command to support more context types.

    lttng add-context -u -e NAME --pid PID
        -t, --type TYPE     Context type.
                            TYPE can be one of the strings below:
                                    all registers
                                    register with name reg_name
                                    variable at addr with field name var_name
                                    stack backtrace


    # lttng add-context -u --pid PID -e aname -t reg:rax
    # lttng add-context -u --pid PID -e aname -t int:varname:0xdeadbeef
    # lttng add-context -u --pid PID -e aname -t string:varname:0xdeadbeef
    # lttng add-context -u --pid PID -e aname -t backtrace

These context types appear in other tracers [4],[5],[6], however
implement all of
them may be not possible within the GSoC schedule.

The above proposed command line interface require defining tracepoint event at
runtime thus free user form compiling tracepoint probes themselves. But I have
a concern such runtime defined tracepoint event mechanism may eventually evolve
to a tiny compiler/interpreter like Systemtap [7] or gdb [8].


After comparing dyninst with gdb's tracepoint code, I think using dyninst as
current choice of implementing dynamic instrumentation is more appropriate.
gdb's tracepoint function relies on a lot of support code they already have,
such as disassembler, elf/DWARF parser. We'll need to a lot of work to port all
the support code.

The underlying technique of these two are quite similar. I will try if I can
isolate interfaces to make changing back-end of dynamic instrumentation easier
or even configurable.

The technique behind scene is simple. We replace the instrumented instruction
with a jmp to redirect the control flow to a trampoline. After saving registers
in the trampoline, we call the probe callback function. Then restore the saved
registers, execute the original instruction and jmp back to the original place.

I will give a simple example here.

Suppose function do_stuff has the following assembly code:

    0x0000000000400a30 <+0>:     push   %rbx
    0x0000000000400a31 <+1>:     mov    $0x400b34,%esi
    0x0000000000400a36 <+6>:     mov    $0x400b36,%edi
    0x0000000000400a3b <+11>:    callq  0x4007b0 <fopen at plt>
    0x0000000000400a62 <+16>:    ret

After executing the following command:

    # lttng enable-event aname -u --pid 8964 --probe do_stuff+0x6

function do_stuff will become:

    0x0000000000400a30 <+0>:     push   %rbx
    0x0000000000400a31 <+1>:     mov    $0x400b34,%esi
    0x0000000000400a36 <+6>:     jmpq   0x1002a             # jmp to trampoline
    0x0000000000400a3b <+11>:    callq  0x4007b0 <fopen at plt>
    0x0000000000400a62 <+16>:    ret

    (gdb) x/100i 0x1002a
    0x1002a: push   %rax                     # save registers
    0x1003e: push   %r15
    0x1004f: movabs $0x7f682f61eac7,%rbx
    0x10059: callq  *%rbx                    # call lttng probe
    0x10060: pop    %r15                     # restore registers
    0x10075: pop    %rax
    0x1007a: mov    $0x400b36,%edi           # this is the original instruction
    0x1007f: jmpq   0x0000000000400a3b       # jmp back

This is an oversimplified example. In reality, we need to do more than that,
like aligning stack. You can refer [3] for a detailed line-by-line analysis
on dyninst's behavior.

[1]: http://bugs.lttng.org/projects/lttng-tools/wiki
[2]: http://bugs.lttng.org/issues/15
[3]: https://github.com/5kg/lttng-gsoc/blob/master/notes/dyninst.md
[4]: http://sourceware.org/systemtap/SystemTap_Beginners_Guide/utargetvariable.html
[5]: http://sourceware.org/systemtap/SystemTap_Beginners_Guide/ustack.html
[6]: http://sourceware.org/gdb/onlinedocs/gdb/Tracepoint-Actions.html#Tracepoint-Actions
[7]: http://sourceware.org/systemtap/SystemTap_Beginners_Guide/understanding-how-systemtap-works.html#understanding-architecture-tools
[8]: http://sourceware.org/gdb/onlinedocs/gdb/Agent-Expressions.html

仝子飞 (Zifei Tong)

More information about the lttng-dev mailing list