Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stackcount: Support uprobes, tracepoints, and USDT #730

Merged
merged 11 commits into from
Oct 5, 2016

Conversation

goldshtn
Copy link
Collaborator

@goldshtn goldshtn commented Oct 5, 2016

stackcount now supports uprobes, tracepoints, and USDT probes (using the bcc/BPF native tracepoint and USDT support). Examples and man page updated. Also fixed a minor bug in symbol enumeration (bcc).

Resolves #580.

A couple of highlights to try:

stackcount t:sched:sched_switch
stackcount -p $(pidof node) u:node:gc__start
stackcount -p $(pidof ruby) u:ruby:object__create # not great stacks though
stackcount u:pthread:pthread_create

Add support for user-space functions in `stackcount` by taking an additional
`-l` command-line parameter specifying the name of the user-space library.
When a user-space library is specified, `stackcount` attaches to a specific
process and traces a user-space function with user-space stacks only.
Regex support for uprobes (similar to what is available for kprobes) is
not currently provided.

Also add a couple of functions to the `BPF` object for consistency.
attach_kprobe allows a regular expression for the function name,
while attach_uprobe does not. Add support in libccc for enumerating
all the function symbols in a binary, and use that in the BPF module
to attach uprobes according to a regular expression. For example:

```python
bpf = BPF(text="...")
bpf.attach_uprobe(name="c", sym_re=".*write$", fn_name="probe")
```
Modify attach_tracepoint to take a regex argument, in which case
it enumerates all tracepoints matching that regex and attaches to
all of them. The logic for enumerating tracepoints should eventually
belong in libccc and be shared across all the tools (tplist, trace
and so on).
bcc_elf would not terminate the enumeration correctly when the
user-provided callback returned -1 but there were still more
sections remaining in the ELF to be enumerated.
Refactored stackcount and added support for uprobes and tracepoints,
which also required changes to the BPF module. USDT support still
pending.
Refactor symbol listing from paging style to foreach-style with a
callback function per-symbol. Even though we're now performing a
callback from C to Python for each symbol, this is preferable to the
paging approach because we need all the symbols in the current use
case.

Also refactored `stackcount` slightly; only missing support for USDT
probes now.
For user-space functions, or when requested for kernel-space
functions or tracepoints, group the output by process. Toggled
with the -P switch, off by default (except for user-space).
Now, stackcount supports USDT tracepoints in addition to
kernel functions, user functions, and kernel tracepoints.
The format is the same as with the other general-purpose
tools (argdist, trace):

```
stackcount -p $(pidof node) u:node:gc*
stackcount -p 185 u:pthread:pthread_create
```
Add examples and man page documentation for kernel
tracepoints, USDT tracepoints, and other features.
When -p is specified, don't print the comm and pid. Also,
when -P is specified for kernel probes (kprobes and
tracepoints), use -1 for symbol resolution so that we
don't try to resolve kernel functions as user symbols.
Finally, print the comm and pid at the end of the stack
output and not at the beginning.
@4ast
Copy link
Member

4ast commented Oct 5, 2016

buildbot, test this please

@4ast
Copy link
Member

4ast commented Oct 5, 2016

buildbot, retest this please

@4ast
Copy link
Member

4ast commented Oct 5, 2016

LGTM. @brendangregg pls review

@brendangregg
Copy link
Member

Tested, works, is awesome. A minor note: I don't like the ordering of user stacks & PIDs:

# ./stackcount.py -p 22487 -r 'c:str.*'
Tracing 38 functions for "c:str.*"... Hit Ctrl-C to end.
^C
    bash [22487]
  strlen
  [unknown]
    2
[...]

I'd prefer having the "bash [22487]" line last, not first. That way, you're reading from smallest to biggest context: what's on-CPU, its parent, and so on, until thread_start, then the parent process ID. Seems more logical that way.

For folded output (stackcount is missing a -f, should add it sometime), it follows a similar order, just reversed: you have comm-PID;thread_start;...;on-CPU_func, and then when you make a flame graph you have the rectangles for each process at the bottom, and towers on top.

Also, since I matched a PID with -p, I'm not sure I'd include the "bash [22487]" line in the output anyway. Redundant. I'd do that if it's a user probe without the -p. I don't feel strongly about this bit.

...

Update: ok, I just broke something. I'd run my ftrace uprobe tool to check something (double check why I was seeing no pthread_create's), and then stackcount didn't work. Weird.

(root) /mnt/src/g/bcc/tools # /apps/perf-tools/bin/uprobe p:libpthread:pthread_create
Tracing uprobe pthread_create (p:pthread_create /lib/x86_64-linux-gnu/libpthread-2.23.so:0x79d0). Ctrl-C to end.
^C
Ending tracing...

(root) /mnt/src/g/bcc/tools # ./stackcount.py c:strlen
Traceback (most recent call last):
  File "./stackcount.py", line 19, in <module>
    from bcc import BPF, USDT
  File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 28, in <module>
    from .libbcc import lib, _CB_TYPE, bcc_symbol, _SYM_CB_TYPE
  File "/usr/lib/python2.7/dist-packages/bcc/libbcc.py", line 133, in <module>
    lib.bcc_foreach_symbol.restype = ct.c_int
  File "/usr/lib/python2.7/ctypes/__init__.py", line 375, in __getattr__
    func = self.__getitem__(name)
  File "/usr/lib/python2.7/ctypes/__init__.py", line 380, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /usr/lib/x86_64-linux-gnu/libbcc.so.0: undefined symbol: bcc_foreach_symbol

(root) /mnt/src/g/bcc/tools # ./stackcount.py 
Traceback (most recent call last):
  File "./stackcount.py", line 19, in <module>
    from bcc import BPF, USDT
  File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 28, in <module>
    from .libbcc import lib, _CB_TYPE, bcc_symbol, _SYM_CB_TYPE
  File "/usr/lib/python2.7/dist-packages/bcc/libbcc.py", line 133, in <module>
    lib.bcc_foreach_symbol.restype = ct.c_int
  File "/usr/lib/python2.7/ctypes/__init__.py", line 375, in __getattr__
    func = self.__getitem__(name)
  File "/usr/lib/python2.7/ctypes/__init__.py", line 380, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /usr/lib/x86_64-linux-gnu/libbcc.so.0: undefined symbol: bcc_foreach_symbol

How did I manage that? ... I did another "make install" of bcc, and it fixed it.

It's reproducable:

(root) /mnt/src/g/bcc/tools # /apps/perf-tools/bin/uprobe p:libpthread:pthread_create
Tracing uprobe pthread_create (p:pthread_create /lib/x86_64-linux-gnu/libpthread-2.23.so:0x79d0). Ctrl-C to end.
^C
Ending tracing...

(root) /mnt/src/g/bcc/tools # ./stackcount.py 
Traceback (most recent call last):
  File "./stackcount.py", line 19, in <module>
    from bcc import BPF, USDT
  File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 28, in <module>
    from .libbcc import lib, _CB_TYPE, bcc_symbol, _SYM_CB_TYPE
  File "/usr/lib/python2.7/dist-packages/bcc/libbcc.py", line 133, in <module>
    lib.bcc_foreach_symbol.restype = ct.c_int
  File "/usr/lib/python2.7/ctypes/__init__.py", line 375, in __getattr__
    func = self.__getitem__(name)
  File "/usr/lib/python2.7/ctypes/__init__.py", line 380, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /usr/lib/x86_64-linux-gnu/libbcc.so.0: undefined symbol: bcc_foreach_symbol

Is this just a separate issue?

@goldshtn
Copy link
Collaborator Author

goldshtn commented Oct 5, 2016

@brendangregg I just tested this with your uprobe tool, and I can't reproduce it. After running uprobe multiple times, stackcount still works.

@goldshtn
Copy link
Collaborator Author

goldshtn commented Oct 5, 2016

@brendangregg Latest commit addresses your comments and also fixes a bug with -P incorrectly parsing symbols for kernel functions.

@brendangregg
Copy link
Member

@goldshtn thanks, this patchset LGTM.

My other issue is still a mystery: libbcc.so.0 gets overwritten when I run my perf-tools uprobe program. But I'm assuming at this point it's unrelated to this patchset.

(root) /mnt/src/g/bcc/tools # cksum /usr/lib/x86_64-linux-gnu/libbcc.so.0; ls -Ll /usr/lib/x86_64-linux-gnu/libbcc.so.0
2276073722 41333736 /usr/lib/x86_64-linux-gnu/libbcc.so.0
-rw-r--r-- 1 root root 41333736 Oct  5 16:08 /usr/lib/x86_64-linux-gnu/libbcc.so.0

(root) /mnt/src/g/bcc/tools # /apps/perf-tools/bin/uprobe p:libpthread:pthread_create
Tracing uprobe pthread_create (p:pthread_create /lib/x86_64-linux-gnu/libpthread-2.23.so:0x79d0). Ctrl-C to end.
^C
Ending tracing...

(root) /mnt/src/g/bcc/tools # cksum /usr/lib/x86_64-linux-gnu/libbcc.so.0; ls -Ll /usr/lib/x86_64-linux-gnu/libbcc.so.0
1302383183 41333656 /usr/lib/x86_64-linux-gnu/libbcc.so.0
-rw-r--r-- 1 root root 41333656 Oct  1 20:50 /usr/lib/x86_64-linux-gnu/libbcc.so.0

Ok, using bcc to debug bcc (again):

/mnt/src/g/bcc/tools # ./ext4slower.py 0 > out.ext4
/mnt/src/g/bcc/tools # grep libbcc out.ext4
20:04:49 ldconfig.real  26981  O 0       0           0.00 libbcc.so.0.1.7
20:04:51 cksum          26990  O 0       0           0.00 libbcc.so.0.2.0
20:04:51 cksum          26990  R 65536   0           0.05 libbcc.so.0.2.0
20:04:51 cksum          26990  R 65536   64          0.01 libbcc.so.0.2.0
20:04:51 cksum          26990  R 65536   128         0.01 libbcc.so.0.2.0
20:04:51 cksum          26990  R 65536   192         0.01 libbcc.so.0.2.0
20:04:51 cksum          26990  R 65536   256         0.01 libbcc.so.0.2.0
[...]

/mnt/src/g/bcc/tools # ./execsnoop.py 
PCOMM            PID    PPID   RET ARGS
uprobe           27246  26565    0 /apps/perf-tools/bin/uprobe p:libpthread:pthread_create
uname            27247  27246    0 /bin/uname -r
which            27248  27246    0 /usr/bin/which file
which            27249  27246    0 /usr/bin/which objdump
which            27250  27246    0 /usr/bin/which ldconfig
which            27251  27246    0 /usr/bin/which awk
which            27252  27246    0 /usr/bin/which libpthread
ldconfig         27254  27253    0 /sbin/ldconfig -v
awk              27255  27253    0 /usr/bin/awk -v lib=libpthread 
            $1 ~ /:/ { sub(/:/, "", $1); path = $1 }
            { sub(/\..*/, "", $1); }
            $1 == lib { print path "/" $3 }
ldconfig.real    27254  27253    0 /sbin/ldconfig.real -v
objdump          27257  27256    0 /usr/bin/objdump -tT /lib/x86_64-linux-gnu/libpthread-2.23.so
awk              27258  27256    0 /usr/bin/awk -v sym=pthread_create 
        $NF == sym && $4 == ".text"  { print $1; exit }
file             27260  27246    0 /usr/bin/file /lib/x86_64-linux-gnu/libpthread-2.23.so
cat              27261  27246    0 /bin/cat trace_pipe
rm               27262  27246    0 /bin/rm /var/tmp/.ftrace-lock

(hm, that execsnoop output should strip \n's, and probably truncate to sum limit unless a -w wide option is provided.)

Ok, so it reproduces with just "/sbin/ldconfig.real -v".

Oh, duh:

# make install
[...]
# ls -l /usr/lib/x86_64-linux-gnu/libbcc.so.0
lrwxrwxrwx 1 root root 15 Oct  5 20:17 /usr/lib/x86_64-linux-gnu/libbcc.so.0 -> libbcc.so.0.1.7
# /sbin/ldconfig.real -v > /dev/null
/sbin/ldconfig.real: Path `/lib/x86_64-linux-gnu' given more than once
/sbin/ldconfig.real: Path `/usr/lib/x86_64-linux-gnu' given more than once
/sbin/ldconfig.real: /lib/x86_64-linux-gnu/ld-2.23.so is the dynamic linker, ignoring

# ls -l /usr/lib/x86_64-linux-gnu/libbcc.so.0
lrwxrwxrwx 1 root root 15 Oct  5 20:17 /usr/lib/x86_64-linux-gnu/libbcc.so.0 -> libbcc.so.0.2.0

It's redoing the symlink. I think this is only something I'd see with my dev system.

ok @4ast , looks good to merge. Thanks @goldshtn !

@4ast 4ast merged commit 07175d0 into iovisor:master Oct 5, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Generalize stackcount to support uprobes and tracepoints
3 participants