8

I'd like to collect stacktraces from my Java app for creating CPU Flame Graphs for profiling.

This is very similar to this question: How to get complete stack dump from profiler in every sample for use in flame graph? with 2 differences:

  1. I work with Java code and I need Java stacktraces
  2. I'm working on Mac (this means there is no pref and AFAIK dtrace on OSX doesn't support jstackextension).

I have already tried lightweight-java-profiler and Honest profiler, and both of them don't seem to work on Mac. I also Tried VisualVM, but I couldn't get it to produce the stacktrace dumps that I needed.

First prioirty for me are flame graphs generated from Java stacktraces, but having the native call stack as well would be great, because it would let me address the I/O issues (and maybe even generate hot/cold flame graphs).

rogerdpack
  • 50,731
  • 31
  • 212
  • 332
mik01aj
  • 10,150
  • 11
  • 65
  • 109

4 Answers4

5

Good news, the FlameGraph repository has "a script" to work with jstacks already in it.

https://github.com/brendangregg/FlameGraph

It's the stackcollapse-jstack.pl.

It seems that by default it expects just stack trace after stack trace in its input, and counts each one as "a sample point."

So you can just do multiple jstack's into a file (run this once or a few times, or once a second "for awhile" etc.):

jstack pid_of_your_jvm >> my_jstack

Then execute that script:

 ./stackcollapse-jstack.pl my_jstack > my_jstack.folded

and finally convert to flamegraph:

 ./flamegraph.pl --color=java my_jstack.folded > my_jstack.svg

No third party helpers required (though they may still be useful).

Note also that the stackcollapse-jstack.pl file discards non RUNNABLE threads, you may want to tweak that if you want to also include "idle" threads (typically you don't).

Apparently you could use the linux "perf" command to generate stacks for a java process, as well, see the README https://github.com/brendangregg/FlameGraph

This might include more native calls, for instance.

rogerdpack
  • 50,731
  • 31
  • 212
  • 332
4

I created 2 little shell scripts based on @cello's answer. They generate hot/cold flame graphs.

Get them from this Gist.

Usage:

ps ax | grep java # find the PID of your process
./profile.sh 20402 stacks.txt
./gen.sh stacks.txt

Alternatively, to measure application from startup (in this, case, my gradle build that also needed to be run in another directory and with some input stream) I used:

cd ../my-project; ./gradlew --no-daemon clean build < /dev/zero &; cd -; ./profile.sh $! stacks.txt
./gen.sh stacks.txt

Results:

flame graphs

In this example, I can clearly see that my application is I/O bound (notice blue bars on top).

mik01aj
  • 10,150
  • 11
  • 65
  • 109
4

Try this: https://github.com/saquibkhan/javaFlameGraph

Installation

npm install javaflamegraph

Usage

  1. cd javaflamegraph
  2. npm start - This will wait till it detects a process with name 'Java'. Can be best best used to start profiling at program startup.
  3. npm run start <process id> - This will start profiling for the given process id. e.g. npm run start 1234
mik01aj
  • 10,150
  • 11
  • 65
  • 109
saquib khan
  • 151
  • 3
3

Did you try the jstack command? just run it on the command line: jstack pidOfJavaProcess > stack.txt (naturally, replacing pidOfJavaProcess with the actual process number). You could run this in a loop in bash (the default shell used on Mac OS X):

while true; do jstack pidOfJavaProcess >> stack.txt; sleep 1.0; done

note the >> to append to the file, and not overwrite it each second. Press Ctrl+C to stop logging the stack traces.

This will only generate the java stack traces, and not the native call stacks from the JVM.

rogerdpack
  • 50,731
  • 31
  • 212
  • 332
cello
  • 4,948
  • 3
  • 21
  • 26
  • Thanks! With this answer I was able to create the flame graph (I used `sleep 0.001`). It's not very convenient (e.g. it's hard to profile application startup), but works :) An extended answer would be highly appreciated, though. – mik01aj Dec 04 '14 at 21:14
  • Is the hard part to profile the application startup the problem to get the process-id quick enough? If yes: How do you start your application? from an IDE (Eclipse/Netbeans/...), or from the command line/Terminal? If it's from the Terminal, with bash you could use `$!` to get the process number of the last started process. Putting the java-command and the above one-liner into a shell-file, and changing the one-liner to `while true; jstack $! >> stack.txt; sleep 0.001; done` and then starting that shell-script could reduce the lost time at startup to a minimum, I guess. – cello Dec 04 '14 at 21:26