Skip to content

Commit ca560cb

Browse files
async-profiler#1174: Handle feedback & remove dependency on java version
1 parent 42aad62 commit ca560cb

File tree

4 files changed

+66
-63
lines changed

4 files changed

+66
-63
lines changed

src/vmEntry.cpp

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -106,48 +106,32 @@ static void* resolveMethodIdEnd() {
106106
return NULL;
107107
}
108108

109-
void VM::initWrapper(JavaVM* vm) {
110-
JNIEnv* env;
111-
112-
jint get_env_result = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
113-
if (get_env_result == JNI_OK) {
114-
VM::init(vm, true);
115-
return;
116-
} else if (get_env_result != JNI_EDETACHED) {
117-
return;
118-
}
119-
120-
// VM exists but not attached to current thread
121-
122-
// Work around for JDK-8308341, as JNI might return a none initialized VM so we check for the existence of certain VM threads
123-
// "VM Thread" and "Service Thread" in this case are used as indication that the VM is in a good enough state to be used
124-
char thread_name[4096];
109+
// Work around for JDK-8308341, as JNI might return a none initialized VM so we check for the existence of certain VM threads
110+
// "VM Thread" and "Service Thread" in this case are used as indication that the VM is in a good enough state to be used
111+
bool VM::checkJvmThreads() {
112+
char thread_name[20];
125113
int thread_name_offset = 0;
126114
bool vm_thread = false, service_thread = false;
127115

128116
ThreadList* list = OS::listThreads();
129117
while (list->hasNext()) {
130-
if (!OS::threadName(list->next(), thread_name, 4096)) {
118+
if (!OS::threadName(list->next(), thread_name, sizeof(thread_name))) {
131119
continue;
132120
}
133121

134122
// For MacOs Thread name starts with "Java: "
135-
if (!OS::isLinux() && thread_name[0] == 'J' && strncmp(thread_name, "Java: ", 6) == 0) {
123+
if (strncmp(thread_name, "Java: ", 6) == 0) {
136124
thread_name_offset = 6;
137125
}
138126

139-
if (thread_name[thread_name_offset] == 'V' && strcmp(thread_name + thread_name_offset, "VM Thread") == 0) {
127+
if (strcmp(thread_name + thread_name_offset, "VM Thread") == 0) {
140128
vm_thread = true;
141-
} else if (thread_name[thread_name_offset] == 'S' && strcmp(thread_name + thread_name_offset, "Service Thread") == 0) {
129+
} else if (strcmp(thread_name + thread_name_offset, "Service Thread") == 0) {
142130
service_thread = true;
143131
}
144132
}
145133

146-
// Check that VM is in a good state to attach the thread
147-
if (service_thread && vm_thread && vm->AttachCurrentThreadAsDaemon((void**)&env, NULL) == JNI_OK) {
148-
VM::init(vm, true);
149-
return;
150-
}
134+
return vm_thread && service_thread;
151135
}
152136

153137
bool VM::init(JavaVM* vm, bool attach) {
@@ -514,6 +498,7 @@ JNI_OnUnload(JavaVM* vm, void* reserved) {
514498
// Try to find a running JVM instance & attach it to the profiler
515499
void VM::tryAttach() {
516500
JavaVM* jvm;
501+
JNIEnv* env;
517502
jsize nVMs;
518503

519504
if (_getJvm == NULL) {
@@ -530,5 +515,18 @@ void VM::tryAttach() {
530515
if (result != JNI_OK || nVMs != 1) {
531516
return;
532517
}
533-
VM::initWrapper(jvm);
518+
519+
// Check the current JNI environment status
520+
jint get_env_result = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
521+
if (get_env_result == JNI_OK) {
522+
VM::init(jvm, true);
523+
return;
524+
} else if (get_env_result != JNI_EDETACHED) {
525+
return;
526+
}
527+
528+
// Here JVM exists but is not attached to current thread
529+
if (VM::checkJvmThreads() && jvm->AttachCurrentThreadAsDaemon((void**)&env, NULL) == JNI_OK) {
530+
VM::init(jvm, true);
531+
}
534532
}

src/vmEntry.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class VM {
113113
static void loadMethodIDs(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass);
114114
static void loadAllMethodIDs(jvmtiEnv* jvmti, JNIEnv* jni);
115115

116-
static void initWrapper(JavaVM* vm);
116+
static bool checkJvmThreads();
117117

118118
public:
119119
static AsyncGetCallTrace _asyncGetCallTrace;

test/one/profiler/test/TestProcess.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public class TestProcess implements Closeable {
3434
public static final String TESTBIN = "%testbin";
3535

3636
private static final String JAVA_HOME = System.getProperty("java.home");
37-
private static final String JAVA_VERSION = Runner.detectJvmVersion() + "";
3837

3938
private static final Pattern filePattern = Pattern.compile("(%[a-z]+)(\\.[a-z]+)?");
4039

@@ -90,7 +89,6 @@ public TestProcess(Test test, Os currentOs, String logDir) throws Exception {
9089
}
9190
}
9291
pb.environment().put("TEST_JAVA_HOME", JAVA_HOME);
93-
pb.environment().put("TEST_JAVA_VERSION", JAVA_VERSION);
9492

9593
this.p = pb.start();
9694

test/test/nonjava/non_java_app.cpp

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,16 @@
1010
#include <limits.h>
1111
#include "asprof.h"
1212
#include <pthread.h>
13-
14-
// detect arch for java 8
15-
#if defined(__x86_64__) || defined(_M_X64)
16-
# define JAVA8_ARCH_PATH "amd64/"
17-
#elif defined(__i386) || defined(_M_IX86)
18-
# define JAVA8_ARCH_PATH "i386/"
19-
#elif defined(__aarch64__) || defined(_M_ARM64)
20-
# define JAVA8_ARCH_PATH "aarch64/"
21-
#elif defined(__arm__) || defined(_M_ARM)
22-
# define JAVA8_ARCH_PATH "arm/"
23-
#else
24-
# define JAVA8_ARCH_PATH "/"
25-
#endif
13+
#include <time.h>
14+
#include <dirent.h>
15+
#include <string.h>
2616

2717
#ifdef __linux__
2818
const char profiler_lib_path[] = "build/lib/libasyncProfiler.so";
29-
const char jvm_lib_path[] = "lib/server/libjvm.so";
30-
const char jvm8_lib_path[] = "lib/" JAVA8_ARCH_PATH "server/libjvm.so";
19+
const char jvm_lib_path[] = "server/libjvm.so";
3120
#else
3221
const char profiler_lib_path[] = "build/lib/libasyncProfiler.dylib";
33-
const char jvm_lib_path[] = "lib/server/libjvm.dylib";
34-
const char jvm8_lib_path[] = "lib/server/libjvm.dylib";
22+
const char jvm_lib_path[] = "server/libjvm.dylib";
3523
#endif
3624

3725
typedef jint (*CreateJvm)(JavaVM **, void **, void *);
@@ -45,8 +33,6 @@ JNIEnv* _env;
4533

4634
void* _jvm_lib;
4735

48-
jint java_version = 0;
49-
5036
void outputCallback(const char* buffer, size_t size) {
5137
fwrite(buffer, sizeof(char), size, stderr);
5238
}
@@ -84,30 +70,49 @@ void stopProfiler(char* outputFile) {
8470
}
8571

8672
void loadJvmLib() {
73+
char lib_path[PATH_MAX];
74+
8775
// Get Java home
8876
char* java_home = getenv("TEST_JAVA_HOME");
8977
if (java_home == NULL) {
9078
std::cerr << "TEST_JAVA_HOME is not set" << std::endl;
9179
exit(1);
9280
}
93-
// Get Java version
94-
char* version = getenv("TEST_JAVA_VERSION");
95-
if (version == NULL) {
96-
std::cerr << "TEST_JAVA_VERSION is not set" << std::endl;
97-
exit(1);
81+
82+
// check that libjvm is found under the standard path
83+
snprintf(lib_path, sizeof(lib_path), "%s/%s/%s", java_home, "lib", jvm_lib_path);
84+
if ((_jvm_lib = dlopen(lib_path, RTLD_LOCAL | RTLD_NOW)) != NULL) {
85+
return;
9886
}
99-
// Java 8 or higher
100-
java_version = std::stoi(version);
101-
if (java_version < 8) {
102-
std::cerr << "Unsupported Java version: " << version << std::endl;
87+
88+
char java_lib_home[PATH_MAX];
89+
struct dirent* entry;
90+
DIR* dir;
91+
92+
// libjvm wasn't found under standard path, this could happen in JDK 8 where the path is formated like:
93+
// ${TEST_JAVA_HOME}/lib/${ARCH}/server/libjvm.(so|dylib)
94+
snprintf(java_lib_home, sizeof(java_lib_home), "%s/lib", java_home);
95+
dir = opendir(java_lib_home);
96+
if (dir == NULL) {
97+
std::cerr << "Error opening directory: " << java_lib_home << std::endl;
10398
exit(1);
10499
}
105100

106-
char lib_path[PATH_MAX];
107-
snprintf(lib_path, sizeof(lib_path), "%s/%s", java_home, java_version == 8 ? jvm8_lib_path : jvm_lib_path);
108-
_jvm_lib = dlopen(lib_path, RTLD_LOCAL | RTLD_NOW);
101+
while((entry = readdir(dir)) != NULL) {
102+
// Skip .. & .
103+
if (strcmp(entry->d_name, "..") == 0 || strcmp(entry->d_name, ".") == 0) {
104+
continue;
105+
}
106+
107+
snprintf(lib_path, sizeof(lib_path), "%s/%s/%s", java_lib_home, entry->d_name, jvm_lib_path);
108+
if ((_jvm_lib = dlopen(lib_path, RTLD_LOCAL | RTLD_NOW)) != NULL) {
109+
break;
110+
}
111+
}
112+
113+
// libjvm was never found
109114
if (_jvm_lib == NULL) {
110-
std::cerr << "Unable to find: " << lib_path << ", Error: " << dlerror() << std::endl;
115+
std::cerr << "Unable to find: libjvm" << std::endl;
111116
exit(1);
112117
}
113118
}
@@ -317,6 +322,8 @@ The profiler should be able to profile the JVM task
317322
The JVM is loaded and started before the profiling session is started so it's attached correctly at the session start
318323
*/
319324
void testFlow4(int argc, char** argv) {
325+
struct timespec wait_time = {(time_t)(2), 0L};
326+
320327
validateArgsCount(argc, 3, "Minimum Arguments is 2");
321328

322329
loadProfiler();
@@ -325,12 +332,12 @@ void testFlow4(int argc, char** argv) {
325332
pthread_create(&thread, NULL, jvmThreadWrapper, NULL);
326333

327334
// busy wait for JVM to start on the thread
328-
for (int i = 0; i < 3; i++) for (int i = 0; i < 1000000000; ++i) {}
335+
nanosleep(&wait_time, NULL);
329336

330337
startProfiler();
331338

332339
// busy wait for profiler to sample JVM
333-
for (int i = 0; i < 3; i++) for (int i = 0; i < 1000000000; ++i) {}
340+
nanosleep(&wait_time, NULL);
334341

335342
stopProfiler(argv[2]);
336343
}

0 commit comments

Comments
 (0)