8201655: Add thread-enabled support for the Heap Sampling
Added thread-enabled support Reviewed-by: amenkov, sspitsyn
This commit is contained in:
parent
d1951aa97c
commit
f3657753d6
@ -13595,7 +13595,7 @@ myInit() {
|
||||
</event>
|
||||
|
||||
<event label="Sampled Object Allocation"
|
||||
id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" num="86" since="11">
|
||||
id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" filtered="thread" num="86" since="11">
|
||||
<description>
|
||||
Sent when an allocated object is sampled.
|
||||
By default, the sampling interval is set to 512KB. The sampling is semi-random to avoid
|
||||
|
@ -96,7 +96,8 @@ static const jlong EXCEPTION_BITS = EXCEPTION_THROW_BIT | EXCEPTION_CATCH_BIT;
|
||||
static const jlong INTERP_EVENT_BITS = SINGLE_STEP_BIT | METHOD_ENTRY_BIT | METHOD_EXIT_BIT |
|
||||
FRAME_POP_BIT | FIELD_ACCESS_BIT | FIELD_MODIFICATION_BIT;
|
||||
static const jlong THREAD_FILTERED_EVENT_BITS = INTERP_EVENT_BITS | EXCEPTION_BITS | MONITOR_BITS |
|
||||
BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT;
|
||||
BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT |
|
||||
SAMPLED_OBJECT_ALLOC_BIT;
|
||||
static const jlong NEED_THREAD_LIFE_EVENTS = THREAD_FILTERED_EVENT_BITS | THREAD_START_BIT;
|
||||
static const jlong EARLY_EVENT_BITS = CLASS_FILE_LOAD_HOOK_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT |
|
||||
VM_START_BIT | VM_INIT_BIT | VM_DEATH_BIT | NATIVE_METHOD_BIND_BIT |
|
||||
|
@ -2567,6 +2567,11 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
|
||||
}
|
||||
|
||||
void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
|
||||
JvmtiThreadState *state = thread->jvmti_thread_state();
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
|
||||
("[%s] Trg sampled object alloc triggered",
|
||||
JvmtiTrace::safe_get_thread_name(thread)));
|
||||
@ -2575,14 +2580,16 @@ void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
|
||||
}
|
||||
HandleMark hm(thread);
|
||||
Handle h(thread, object);
|
||||
JvmtiEnvIterator it;
|
||||
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
|
||||
if (env->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) {
|
||||
|
||||
JvmtiEnvThreadStateIterator it(state);
|
||||
for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
|
||||
if (ets->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) {
|
||||
EVT_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
|
||||
("[%s] Evt sampled object alloc sent %s",
|
||||
JvmtiTrace::safe_get_thread_name(thread),
|
||||
object == NULL ? "NULL" : object->klass()->external_name()));
|
||||
|
||||
JvmtiEnv *env = ets->get_env();
|
||||
JvmtiObjectAllocEventMark jem(thread, h());
|
||||
JvmtiJavaThreadEventTransition jet(thread);
|
||||
jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc;
|
||||
|
@ -27,24 +27,40 @@ package MyPackage;
|
||||
/**
|
||||
* @test
|
||||
* @build Frame HeapMonitor ThreadInformation
|
||||
* @summary Ensures the JVMTI Heap Monitor is not thread enable (test to change when it becomes so)
|
||||
* @compile HeapMonitorEventsForTwoThreadsTest.java
|
||||
* @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventsForTwoThreadsTest
|
||||
* @summary Verifies the JVMTI Heap Monitor Thread can disable events for a given thread.
|
||||
* @compile HeapMonitorThreadDisabledTest.java
|
||||
* @run main/othervm/native -Xmx512m -agentlib:HeapMonitorTest MyPackage.HeapMonitorThreadDisabledTest
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HeapMonitorEventsForTwoThreadsTest {
|
||||
public native static boolean checkSamples();
|
||||
public class HeapMonitorThreadDisabledTest {
|
||||
private native static void enableSamplingEvents(Thread thread);
|
||||
private native static boolean checkThreadSamplesOnlyFrom(Thread thread);
|
||||
|
||||
public static void main(String[] args) {
|
||||
final int numThreads = 24;
|
||||
final int numThreads = 4;
|
||||
List<ThreadInformation> threadList = ThreadInformation.createThreadList(numThreads);
|
||||
|
||||
Thread firstThread = threadList.get(0).getThread();
|
||||
Thread secondThread = threadList.get(1).getThread();
|
||||
if (HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread)) {
|
||||
throw new RuntimeException("Sampling event is thread enabled, that is unexpected.");
|
||||
// Sample at a interval of 8k.
|
||||
HeapMonitor.setSamplingInterval(1 << 13);
|
||||
|
||||
// Only enable the sampling for a given thread.
|
||||
Thread thread = threadList.get(0).getThread();
|
||||
enableSamplingEvents(thread);
|
||||
|
||||
System.err.println("Starting threads");
|
||||
ThreadInformation.startThreads(threadList);
|
||||
ThreadInformation.waitForThreads(threadList);
|
||||
System.err.println("Waited for threads");
|
||||
|
||||
// Only have the samples for a given thread should be captured.
|
||||
if (!checkThreadSamplesOnlyFrom(thread)) {
|
||||
throw new RuntimeException(
|
||||
"Problem with checkSamples: got no events from the expected thread");
|
||||
}
|
||||
|
||||
// Now inform each thread we are done and wait for them to be done.
|
||||
ThreadInformation.stopThreads(threadList);
|
||||
}
|
||||
}
|
@ -720,101 +720,86 @@ jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||
#define MAX_THREADS 500
|
||||
|
||||
typedef struct ThreadStats {
|
||||
int number_threads;
|
||||
int thread_count;
|
||||
int counts[MAX_THREADS];
|
||||
int not_helper_counts[MAX_THREADS];
|
||||
int index[MAX_THREADS];
|
||||
jthread threads[MAX_THREADS];
|
||||
|
||||
int method_resolution_problem;
|
||||
char* threads[MAX_THREADS];
|
||||
} ThreadStats;
|
||||
|
||||
static ThreadStats thread_stats;
|
||||
|
||||
static void add_thread_count(jthread thread, int lock, int helper) {
|
||||
int i;
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_MyPackage_HeapMonitorThreadDisabledTest_checkThreadSamplesOnlyFrom(
|
||||
JNIEnv* env, jclass cls, jthread thread) {
|
||||
jvmtiThreadInfo info;
|
||||
const char* name;
|
||||
char* end;
|
||||
int idx;
|
||||
int err;
|
||||
jvmtiError err;
|
||||
char* expected_name;
|
||||
int found_thread = FALSE;
|
||||
|
||||
if (lock) {
|
||||
event_storage_lock(&global_event_storage);
|
||||
err = (*jvmti)->GetThreadInfo(jvmti, thread, &info);
|
||||
expected_name = info.name;
|
||||
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
fprintf(stderr, "Failed to get thread information\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (i = 0; i < thread_stats.number_threads; i++) {
|
||||
if (thread_stats.threads[i] == thread) {
|
||||
if (helper) {
|
||||
thread_stats.counts[i]++;
|
||||
} else {
|
||||
thread_stats.not_helper_counts[i]++;
|
||||
}
|
||||
if (thread_stats.thread_count != 1) {
|
||||
fprintf(stderr, "Wrong thread number: %d (expected 1)\n",
|
||||
thread_stats.thread_count);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lock) {
|
||||
event_storage_unlock(&global_event_storage);
|
||||
}
|
||||
if (strcmp(expected_name, thread_stats.threads[0]) != 0) {
|
||||
fprintf(stderr, "Unexpected thread name: '%s' (expected '%s')\n",
|
||||
thread_stats.threads[0], expected_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void add_thread_count(jthread thread) {
|
||||
int i;
|
||||
jvmtiThreadInfo info;
|
||||
jvmtiError err;
|
||||
|
||||
err = (*jvmti)->GetThreadInfo(jvmti, thread, &info);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
fprintf(stderr, "Thread info for %p failed, ignoring thread count\n",
|
||||
thread);
|
||||
return;
|
||||
}
|
||||
|
||||
event_storage_lock(&global_event_storage);
|
||||
for (i = 0; i < thread_stats.thread_count; i++) {
|
||||
if (!strcmp(thread_stats.threads[i], info.name)) {
|
||||
thread_stats.counts[i]++;
|
||||
event_storage_unlock(&global_event_storage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
thread_stats.threads[thread_stats.number_threads] = thread;
|
||||
thread_stats.threads[thread_stats.thread_count] = info.name;
|
||||
thread_stats.counts[thread_stats.thread_count]++;
|
||||
thread_stats.thread_count++;
|
||||
event_storage_unlock(&global_event_storage);
|
||||
}
|
||||
|
||||
err = (*jvmti)->GetThreadInfo(jvmti, thread, &info);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
if (lock) {
|
||||
event_storage_unlock(&global_event_storage);
|
||||
}
|
||||
|
||||
// Just to have it accounted as an error...
|
||||
info.name = "Allocator99";
|
||||
}
|
||||
|
||||
if (!strstr(info.name, "Allocator")) {
|
||||
if (lock) {
|
||||
event_storage_unlock(&global_event_storage);
|
||||
}
|
||||
|
||||
// Just to have it accounted as an error...
|
||||
info.name = "Allocator98";
|
||||
}
|
||||
|
||||
name = info.name + 9;
|
||||
end = NULL;
|
||||
idx = strtol(name, &end, 0);
|
||||
|
||||
if (*end == '\0') {
|
||||
if (helper) {
|
||||
thread_stats.counts[thread_stats.number_threads]++;
|
||||
} else {
|
||||
thread_stats.not_helper_counts[thread_stats.number_threads]++;
|
||||
}
|
||||
|
||||
thread_stats.index[thread_stats.number_threads] = idx;
|
||||
thread_stats.number_threads++;
|
||||
} else {
|
||||
fprintf(stderr, "Problem with thread name...: %p %s\n", thread, name);
|
||||
}
|
||||
|
||||
if (PRINT_OUT) {
|
||||
fprintf(stderr, "Added %s - %p - %d - lock: %d\n", info.name, thread, idx, lock);
|
||||
}
|
||||
|
||||
if (lock) {
|
||||
event_storage_unlock(&global_event_storage);
|
||||
}
|
||||
JNIEXPORT void JNICALL
|
||||
Java_MyPackage_HeapMonitorThreadDisabledTest_enableSamplingEvents(
|
||||
JNIEnv* env, jclass cls, jthread thread) {
|
||||
fprintf(stderr, "Enabling for %p\n", thread);
|
||||
check_error((*jvmti)->SetEventNotificationMode(
|
||||
jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, thread),
|
||||
"Set event notifications for a single thread");
|
||||
}
|
||||
|
||||
static void print_thread_stats() {
|
||||
int i;
|
||||
event_storage_lock(&global_event_storage);
|
||||
fprintf(stderr, "Method resolution problem: %d\n", thread_stats.method_resolution_problem);
|
||||
fprintf(stderr, "Thread count:\n");
|
||||
for (i = 0; i < thread_stats.number_threads; i++) {
|
||||
fprintf(stderr, "\t%p: %d: %d - %d\n", thread_stats.threads[i],
|
||||
thread_stats.index[i],
|
||||
thread_stats.counts[i],
|
||||
thread_stats.not_helper_counts[i]);
|
||||
for (i = 0; i < thread_stats.thread_count; i++) {
|
||||
fprintf(stderr, "\t%s: %d\n", thread_stats.threads[i], thread_stats.counts[i]);
|
||||
}
|
||||
event_storage_unlock(&global_event_storage);
|
||||
}
|
||||
@ -826,7 +811,7 @@ void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env,
|
||||
jobject object,
|
||||
jclass object_klass,
|
||||
jlong size) {
|
||||
add_thread_count(thread, 1, 1);
|
||||
add_thread_count(thread);
|
||||
|
||||
if (event_storage_get_compaction_required(&global_event_storage)) {
|
||||
event_storage_compact(&global_event_storage, jni_env);
|
||||
@ -864,29 +849,6 @@ static int enable_notifications() {
|
||||
"Set event notifications");
|
||||
}
|
||||
|
||||
static int enable_notifications_for_two_threads(jthread first, jthread second) {
|
||||
if (check_error((*jvmti)->SetEventNotificationMode(
|
||||
jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL),
|
||||
"Set event notifications")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (check_error((*jvmti)->SetEventNotificationMode(
|
||||
jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, first),
|
||||
"Set event notifications")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Second thread should fail.
|
||||
if (check_error((*jvmti)->SetEventNotificationMode(
|
||||
jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, second),
|
||||
"Set event notifications")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
|
||||
jint res;
|
||||
@ -970,14 +932,6 @@ Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) {
|
||||
enable_notifications();
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_MyPackage_HeapMonitor_enableSamplingEventsForTwoThreads(JNIEnv* env,
|
||||
jclass cls,
|
||||
jthread first,
|
||||
jthread second) {
|
||||
return enable_notifications_for_two_threads(first, second);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) {
|
||||
check_error((*jvmti)->SetEventNotificationMode(
|
||||
@ -1130,10 +1084,9 @@ typedef struct sThreadsFound {
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls,
|
||||
jint num_threads) {
|
||||
|
||||
print_thread_stats();
|
||||
// Ensure we got stacks from at least num_threads.
|
||||
return thread_stats.number_threads >= num_threads;
|
||||
return thread_stats.thread_count >= num_threads;
|
||||
}
|
||||
|
||||
JNIEXPORT
|
||||
|
Loading…
x
Reference in New Issue
Block a user