Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using DTrace to get stack traces / profiling data on Rust

I'm trying to get a nice flamegraph of my Rust code. Unfortunately, Xcode 8.3 doesn't support exporting profiling data anymore, so I've been trying to use DTrace to get the profiling data.

I have enabled debug info in my Cargo.toml for the release binaries:

[profile.release]
debug = true

Then I run the release binary (mybinaryname), and sample stack traces using DTrace:

sudo dtrace -n 'profile-997 /execname == "mybinaryname"/ { @[ustack(100)] = count(); }' -o out.user_stacks

The end result is something like this:

          0x10e960500
          0x10e964632
          0x10e9659e0
          0x10e937edd
          0x10e92aae2
          0x10e92d0d7
          0x10e982c8b
          0x10e981fc1
          0x7fff93c70235
          0x1
            1

For comparison, getting traces of iTerm2 gets me nice traces like this:

          CoreFoundation`-[__NSArrayM removeAllObjects]
          AppKit`_NSGestureRecognizerUpdate+0x769
          CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__+0x17
          CoreFoundation`__CFRunLoopDoObservers+0x187
          CoreFoundation`__CFRunLoopRun+0x4be
          CoreFoundation`CFRunLoopRunSpecific+0x1a4
          HIToolbox`RunCurrentEventLoopInMode+0xf0
          HIToolbox`ReceiveNextEventCommon+0x1b0
          HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter+0x47
          AppKit`_DPSNextEvent+0x460
          AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]+0xaec
          AppKit`-[NSApplication run]+0x39e
          AppKit`NSApplicationMain+0x4d5
          iTerm2`main+0x6e
          libdyld.dylib`start+0x1
          iTerm2`0x1
            1

Is it possible to get stack traces with debug info in Rust code? (Xcode's Instruments for sure can see the function names, so they are there!) If it is possible, do I need to do take some additional steps, or am I just doing something wrong?

like image 551
GolDDranks Avatar asked Apr 18 '17 09:04

GolDDranks


1 Answers

I found a workaround and got some insight why it might not have worked, but the reason why is not 100% clear.

The debug symbols that rustc produces can be found in target/release/deps/mybinaryname-hashcode.dSYM. In the same directory there is a binary file target/release/deps/mybinaryname-hashcode to which the symbols correspond to.

The debug symbol finding library on MacOS is highly magical – as is mentioned in the LLDB docs, symbols are found using various methods, including Spotlight search. I'm not even sure which Frameworks are the ones being used by Xcode's Instruments and the bundled DTrace. (There are mentions about frameworks called DebugSymbols.framework and CoreSymbolication.framework.) Because of this magic, I gave up trying to understand why didn't it work.

The workaround is to pass dtrace the -p option along with the PID of the inspected process:

sudo dtrace -p $PID -n 'profile-997 /pid == '$PID'/ { @[ustack(100)] = count(); }' -o $TMPFILE &>/dev/null

Here's the man of -p:

Grab the specified process-ID pid, cache its symbol tables, and exit upon its completion. If more than one -p option is present on the command line, dtrace exits when all commands have exited, reporting the exit status for each process as it terminates. The first process-ID is made available to any D programs specified on the command line or using the -s option through the $target macro variable.

It's not clear why the debug info of various other binaries is shown by default, or why Rust binaries need the -p option, but it does its job as a workaround.

like image 130
GolDDranks Avatar answered Oct 23 '22 10:10

GolDDranks