Is there a way to generate a periodic java thread dump using JVMTI?
Asked Answered
G

4

8

There are multiple ways to generate thread dumps in java.

I'd like to use JVMTI (the C API) to generate it, in order to evaluate its performance impact on a running JVM. (I am aware of jstack and JMX ; this question is not generally about getting thread dumps, but about using the JVMTI API).

I'm basing my code off of this blog post. In there, the java agent attaches to the SIGQUIT signal. I'd like to avoid that, because that is the same signal that the JVM uses in order to write a thread dump to stdout. I want to avoid that duplicity.

In other words, I'd like to either attach to a different signal, or find a way for the agent to generate a thread dump periodically.

Gloaming answered 7/11, 2014 at 6:38 Comment(4)
None of the events in jvmtiEventCallbacks look suitable (unless you want to use DataDumpRequestion, but if you did, you wouldn't be asking here :) ). It looks like you're best off having your agent call GetStackTrace directly. Is there a reason you can't do that?Unlucky
@Paul-Hicks, Could you post a link or code on how I would attach to the jvm in this case?Gloaming
You put the agent dll or .so in your jvm's -agentpath or define it as an -agentlib. Have a look at this answer or IBM's intro page. Is this what you wanted to know? Should I turn this into an answer?Unlucky
ok, so that's pretty much how I've attached to the jvm so far (with minor differences). So I guess that brings me to the next question: How do I get a pointer to a jvmtiEnv, in order to call GetStackTrace, if not from a callback? The point is to call that function periodically.Gloaming
T
2

In there, the java agent attaches to the SIGQUIT signal. I'd like to avoid that, because that is the same signal that the JVM uses in order to write a thread dump to stdout. I want to avoid that duplicity.

Just remove the following snippet from your code

/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.DataDumpRequest = &dumpThreadInfo;
err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
CHECK_JVMTI_ERROR(jvmti, err);
err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,JVMTI_EVENT_DATA_DUMP_REQUEST, NULL);
CHECK_JVMTI_ERROR(jvmti, err);

I'd like to either attach to a different signal

Here is the paper, that is a bit old, but the information should be still relevant.

Just a sample of how to do a signal handling

import sun.misc.Signal;
import sun.misc.SignalHandler;

public class ThreadDumpSignalHandler implements SignalHandler {
    private volatile SignalHandler old;
    private ThreadDumpSignalHandler() {

    }
    public static void register(String sigName) {
        ThreadDumpSignalHandler h = new ThreadDumpSignalHandler();
        h.old = Signal.handle(new Signal(sigName), h)
    }
    public void handle(Signal sig) {
        threadDump();

        if(old != null && old != SIG_DFL && old != SIG_IGN) {
            old.handle(sig);
        }
    }
    // call your own threadDump native method.
    // note that in the implementation of this method you are able to access jvmtiEnv from *gdata (see below)
    private native void threadDump();
}

ThreadDumpSignalHandler.register("INT");

Of cause you can write completely native signal handler (please note that I haven't tested it, this is just an idea that should work)

static sighandler_t old_handler;
static void thread_dump_handler(int signum) {
    if(gdata && gdata->jvmti) {
        ... get thread dump
    }

    if(old_handler) {
        old_handler(signum);
    }
}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
    old_handler = signal(SIGINT, thread_dump_handler);

    ...
}

or find a way for the agent to generate a thread dump periodically.

In your sample there is global *gdata

typedef struct {
   /* JVMTI Environment */
   jvmtiEnv      *jvmti;
   jboolean       vm_is_started;
   /* Data access Lock */
   jrawMonitorID  lock;
} GlobalAgentData;

static GlobalAgentData *gdata;

... so, just obtain jvmtiEnv from there at any time you want (timer callbacks, etc.)

Traumatize answered 16/11, 2014 at 12:26 Comment(5)
Is getting *jvmti from *gdata safe? I'm not so sure about that, but I can give it a try.Gloaming
It should be safe. At least you always can use RawMonitorEnter and RawMonitorExit with gdata->lock, but I don't think it is really necessary until you are going to access gdata->jvmti concurrently.Traumatize
Also, you can create a thread by means of RunAgentThread. The thread's start function accepts jvmtiEnv as its first param. So you can do periodic thread dumps from within this natively created thread.Traumatize
using the *jvmti is 100% safe. The lifetime is speced to be until the agent unloads.Andrien
I would also recommend against using signals - the JVM mucks with a number of them internally, and signal chaining is very hard to get right. Find a different triggering mechanism (like my answer suggests) - eg: socket, semaphore, etc...Andrien
S
2

If your goal is to periodically collect thread dump, you can use Java Flight Recorder, that is part of Java Mission Controller

Starting with the release of Oracle JDK 7 Update 40 (7u40), Java Mission Control is bundled with the HotSpot JVM.

Sickert answered 17/11, 2014 at 1:31 Comment(0)
A
0

The blog entry you cite has most of what you need for JVMTI plumbing. You can use the JVMTIenv from gdata. That is legal. Make sure if you're making JNI calls to have a proper JNIenv for your current thread.

You now need to add a way to get notified to take your action (eg: thread dump). Spin up a thread that listens on a socket, uses inotify, named semaphores, etc.. - something that you can poke from outside.

You can then call dumpThreadInfo() from your event handler loop as you see fit.

Andrien answered 17/11, 2014 at 14:41 Comment(0)
M
-2

None of the events in jvmtiEventCallbacks look suitable (unless you want to use DataDumpRequestion, but if you did, you wouldn't be asking here :ok, so that's pretty much how I've attached to the jvm so far (with minor differences). So I guess that brings me to the next question: How do I get a pointer to a jvmtiEnv, in order to call GetStackTrace, if not from a callback? The point is to call t

Misspeak answered 17/11, 2014 at 1:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.