USDT tools for higher level languages#774
Conversation
|
Some general comments before I get into the tools... Not a lot was done with USDT in runtimes. Here's the three problems:
Java had some interesting ones: like class allocations, thread creations. I think those interest me the most. Sure, I'll take GC scripts and function tracing scripts like existed in the DTraceToolkit, but the latter are more debug-only until we lower overheads. Another interesting use is mixing USDT probes with other user and kernel events, which is something other analyzers can't do. You can imagine doing this for MySQL USDT probes: casting kernel events in the context of a query. Need to think about opportunities in runtimes too. As for naming: I'd go with javagc, javacalls, javaclassstat, nodegc, nodecalls, pythoncalls, etc. So we can list java* for Java developers. Findability. Now if the tools are so close, then symlinks or light 2-line shell wrappers to a common multi-tool? You know what I'd do: I'd start with separate files for each, and consider refactoring later. Think about this: if a Java developer wants to enhance separate Java tools, they can go to town. But if they are all multi-tools, then we'll be telling them: please test your changes with Node.js, Python, etc, etc. I can imagine how that'd go! |
|
Thanks for the feedback! I realize the method tracing tools are going to be for debug use, mostly. Re GC stats, Java sure has a slew of logs, but for other runtimes it can be quite useful. For example, a quick search reveals that Node has a Re kernel events in the context of a query: this can be done today with |
|
Yes, good points about Node.js and Ruby. There's a lot of languages out there. I've been thinking about it, and maybe it does make sense to begin with the multi-tools (and multiple symlinks/shell scripts to them). Because: A) accommodating Java developers with individual scripts is a nice idea, but what's the chance none contribute and we end up doing the work anyway?, and B) bcc tools are long. With DTrace, it was no big deal to have 4 tools that had 95% common code, when the tools were 20 lines each. With bcc, we have 100-300 line tools, so duplicating that is 10x more work to maintain. |
|
@brendangregg So I've added a couple of tools and changed some of the tools I pushed originally. What we have right now (not 100% done, and no documentation yet, so not mergeable):
The last remaining tool I haven't touched yet is I was also thinking of building |
|
May as well do a quick systrace. I did dtruss once, just to show DTrace could do syscalls. Its only feature over Solaris truss was being able to match multiple process names with -n, instead of one PID only. Next thing dtruss is the only syscall tracer for OS X. I say quick, as I suspect strace has a lot of translations in that would be redundant to copy and hard to maintain. So I wouldn't invest too much time in the experiment. What would be also useful (in addition) would be a syscount, like my https://github.com/brendangregg/perf-tools/blob/master/examples/syscount_example.txt . Yes, that's really a more featured version of strace -c. I'd be inclined not to add it to systrace, since that would be mixing a per-event output tool with a summary tool (I know strace has a precedent for that, but I'm not sure it's a good precedent). I like the argdist / trace split. Your choice of USDT tools makes sense. I still don't know whether these should all become symlinks (java_, node_) or just keep them as u*, or both. I took a quick look at the code -- maybe because they are new you just haven't done this yet, but I'd have put big comments above each language block. Like: probes.append(Probe("gc__start", "gc__done",
begin_save, end_save, formatter))
#
# Ruby
#
elif args.language == "ruby":
# Ruby GC probes do not have any additional information available.
probes.append(Probe("gc__mark__begin", "gc__mark__end",
"", "", lambda _: "GC mark stage"))
probes.append(Probe("gc__sweep__begin", "gc__sweep__end",
"", "", lambda _: "GC sweep stage"))
#
# Node.js
#
elif args.language == "node":
end_save = """ |
|
syscount is pretty much there with Re systrace, I didn't mean to provide the same level of detail (with the fd parsing, string extraction and so on). Just a basic trace of each syscall with 6 arguments. More advanced parsing would be possible with |
c681cdc to
1207f28
Compare
|
Just forced-push after rebasing. Added a new tool, Example output: Also, added |
|
Added man pages and example files. This is ready for review and merging. /cc @brendangregg |
|
@goldshtn I tried Maybe it's a problem of my setups? My two machines have oldish kernels (4.4.26 and 4.7.4) and no packaged configured with |
|
@dark Thanks, it's leaking indeed on my 4.9-rc as well. I'll track these fds down. I'm explicitly cleaning up the |
|
Rebased. We should get this merged at some point :) |
|
I'm back... I like the tools. They seem to work. :) uobjnew.py is missing a PID filter, at least for the "c" match (the following python program is idle): uobjnew examples have a "-l", but that should be omitted (will need to update the tool and example file) (update: read ahead as I have a different suggestion about -l): In the future, it would be nice if these tools could pick a default language if one wasn't specified. Eg, if the process name is python or java, then go with that. There will be times you'll want to override it: examining C calls for a Java program, for example. But most of the time I'm using these tools like this: ustat.py, ucalls.py, and uthreads.py want the -l. Should that be dropped for consistency? ... It might be a bit tidier, if these tools could pick the java/python/etc default, to then have the -l as the optional override. In which case, all tools try to pick a default, and all tools have an optional -l. uflow.py: BTW, uflow.py works well, at least for something simple: Those PID/TID columns should be handy for filtering multi-threaded apps. |
|
@goldshtn are you planning one more update before it can be merged? |
|
Yes - will fix the immediate issues and rebase, and leave non-pressing stuff for a separate PR. Hope to wrap this up this week. |
`ucalls` is a new tool that uses the USDT probes in high- level languages (Java, Python, Ruby) to summarize the number of method calls and optionally their latency. This is similar to `funccount` and `funclatency` but for methods in Java, Python, Ruby, and other languages that might have USDT probes for method entry and exit. `ucalls` uses the `method__entry` and `method__return` probes in Java and Ruby, and the `function__entry` and `function__return` probes in Python. The Python probes are not as accurate because they do not report the class name, but are still useful. The optional `-L` switch indicates whether latency numbers are required. By default, only the number of calls to each method is printed. The `-T` switch filters out the top methods (by number of calls or total latency).
`ugc` monitors GC start and end events in Java, Python, and Ruby, printing out a message when a GC cycle completes along with the duration of the GC and an optional additional description that depends on the target language. NOTE: This WIP does not have any interesting additional descriptions yet. Usage example: ``` ugc java 1448 # monitor Java GCs in process 1448 ```
For runtimes that make it available, we now extract GC event information such as heap sizes and print this information in addition to the GC duration and timestamp.
Skeletons for method flow, object allocations, general event statistics, and thread statistics.
Add Node support for gc__start and gc__done probes, which includes GC type flag parsing.
Add `-S` switch for tracing syscalls with or without high- level language method calls. When `-S` is provided, attach to all `sys_` and `SyS_` entry points in the kernel and trace the number of calls and execution latency (if `-L` was also specified). This is now also a useful general-purpose tool for syscall count/latency summarizing when a language is not specified.
`uthreads` attaches to thread start and stop USDT probes and traces out thread creation and destruction events. Currently, only Java is supported.
Trace `pthread_start` calls and get the symbol for the newly-started thread for printing. Also allow no-Java runs with no language specified for pthread-only tracing.
Use various USDT probes in Java and Ruby, and `malloc()` uprobe in C, to summarize object allocations by type or by size. Print statistics at the end or at intervals, with optional trimming of top types by number of instances allocated or number of bytes.
Allow callers to obtain the list of active (enabled) USDT probes from the USDT object, if they need to enable the uprobes by hand without using the bcc attach support. This is required when the same uprobe needs to be enabled in more than one process.
This tool gathers statistics (by pid) on method calls, class loads, garbage collections, thread creations, object allocations, and exceptions raised and prints them in a top-like style. Supported languages currently include Java, Python, Ruby, and Node. Not all languages provide the necessary probes for all event types. Usage examples: ``` ustat -C ustat -l java 1 ustat -l ruby -S objnew ```
This tool traces method executions and prints out messages with a nesting depth indicator that allows relatively easy visualization of program flow. To reduce overhead, `-C` and `-M` switches take class and method prefixes to filter the output by. The filtering takes place in the BPF program, so the unnecessary data never ends up in user space. Usage examples: ``` uflow java 185 uflow -M index python 180 uflow -C 'IRB::Irb' ruby 10041 ```
The /proc/PID/cmdline file has \0 for spaces in the command line, and there may be trailing \0 characters as well. Replace them all with spaces.
If the process in question doesn't have the USDT probes we expect, handle the failure gracefully. After all, it could be a binary compiled without USDT support, the pid could be recycled to some other application, or -- the process name could match, but by accident, and really belong to some other runtime.
When C allocations are traced, attach the `malloc` uprobe only to the requested process -- otherwise, we get allocation information from the entire system.
|
Rebased and fixed the issues found above -- I think this is ready for merging. I will open a separate issue for automatically detecting the language, I don't know if we always want it. In any case I think it belongs in a separate PR. Re the Let's get this merged (I also have a blog post I need to write about these :)) and then iterate? |
|
great stuff |
This is a work-in-progress: please do not merge yet. I'm putting it out there for review and feedback at this time. Some of the tools are not done yet, and none have example and man files at this time.
Inspired by tools from the DTrace toolkit, here are a couple of tools for high-level languages (currently Java, Python, and Ruby) using their USDT probes when available.
The tools I have planned are:
ucalls(in this PR) -- summarize method call numbers and total latencyugc(in this PR) -- monitor and trace GC eventsuflow(planned) -- print method call flowustat(planned) -- print top-like statistics on method calls, class loads, GC events, and so onOn top of these,
profileshould already work with languages that can emit /tmp/perf-PID.map files by resolving stack traces, so there's probably no need for a customuprofile.I think it should be possible to avoid forking each tool for each language, but rather keep a single set of tools with a language parameter. Examples (some of this should already work at this patchset):
Any thoughts or feedback on tool naming, options, and so on would be appreciated. I am mostly trying to mimic the per-language tools from the DTrace toolkit, while recognizing that some things are not currently/yet possible (e.g. syscall tracing in addition to method tracing).
/cc @brendangregg