8201655: Add thread-enabled support for the Heap Sampling

Added thread-enabled support

Reviewed-by: amenkov, sspitsyn
This commit is contained in:
Jean Christophe Beyler 2018-12-14 13:13:06 -08:00
parent d1951aa97c
commit f3657753d6
5 changed files with 100 additions and 123 deletions

View File

@ -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

View File

@ -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 |

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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