Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Introduce an API to access JVM flags directly
  • Loading branch information
jbachorik committed Nov 8, 2024
commit a59fc2689eb6b26560201fbd1261fa025342a3f6
92 changes: 92 additions & 0 deletions ddprof-lib/src/main/cpp/javaApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,95 @@ Java_com_datadoghq_profiler_JavaProfiler_mallocArenaMax0(JNIEnv *env,
jint maxArenas) {
OS::mallocArenaMax(maxArenas);
}


// JVM Flags
// The flag type is encoded as:
// 0 - boolean
// 1 - int
// 2 - uint
// 3 - intx
// 4 - uintx
// 5 - uint64_t
// 6 - size_t
// 7 - double
// 8 - string
// 9 - string list
extern "C" DLLEXPORT jstring JNICALL
Java_com_datadoghq_profiler_JVMAccess_findStringJVMFlag0(JNIEnv *env,
jobject unused,
jstring flagName) {
JniString flag_str(env, flagName);
char** value = static_cast<char**>(JVMFlag::find(flag_str.c_str(), 256)); // types: 2 ^ [8]
if (value != NULL && *value != NULL) {
return env->NewStringUTF(*value);
}
return NULL;
}

extern "C" DLLEXPORT void JNICALL
Java_com_datadoghq_profiler_JVMAccess_setStringJVMFlag0(JNIEnv *env,
jobject unused,
jstring flagName,
jstring flagValue) {
JniString flag_str(env, flagName);
JniString value_str(env, flagValue);
char** value = static_cast<char**>(JVMFlag::find(flag_str.c_str(), 256)); // types: 2 ^ [8]
if (value != NULL) {
*value = strdup(value_str.c_str());
}
}

extern "C" DLLEXPORT jboolean JNICALL
Java_com_datadoghq_profiler_JVMAccess_findBooleanJVMFlag0(JNIEnv *env,
jobject unused,
jstring flagName) {
JniString flag_str(env, flagName);
char* value = static_cast<char*>(JVMFlag::find(flag_str.c_str(), 1)); // types: 2 ^ [0]
if (value != NULL) {
return *value == 1;
}
return false;
}

extern "C" DLLEXPORT void JNICALL
Java_com_datadoghq_profiler_JVMAccess_setBooleanJVMFlag0(JNIEnv *env,
jobject unused,
jstring flagName,
jboolean flagValue) {
JniString flag_str(env, flagName);
char* value = static_cast<char*>(JVMFlag::find(flag_str.c_str(), 1)); // types: 2 ^ [0]
if (value != NULL) {
*value == flagValue ? 1 : 0;
}
}

extern "C" DLLEXPORT jlong JNICALL
Java_com_datadoghq_profiler_JVMAccess_findIntJVMFlag0(JNIEnv *env,
jobject unused,
jstring flagName) {
JniString flag_str(env, flagName);
long* value = static_cast<long*>(JVMFlag::find(flag_str.c_str(), 126)); // types: 2 ^ [1, 2, 3, 4, 5, 6]
if (value != NULL) {
return *value;
}
return 0;
}

extern "C" DLLEXPORT jdouble JNICALL
Java_com_datadoghq_profiler_JVMAccess_findFloatJVMFlag0(JNIEnv *env,
jobject unused,
jstring flagName) {
JniString flag_str(env, flagName);
double* value = static_cast<double*>(JVMFlag::find(flag_str.c_str(), 128)); // types: 2 ^ [7]
if (value != NULL) {
return *value;
}
return 0.0;
}

extern "C" DLLEXPORT jboolean JNICALL
Java_com_datadoghq_profiler_JVMAccess_healthCheck0(JNIEnv *env,
jobject unused) {
return true;
}
20 changes: 19 additions & 1 deletion ddprof-lib/src/main/cpp/vmStructs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ int VMStructs::_vs_low_bound_offset = -1;
int VMStructs::_vs_high_bound_offset = -1;
int VMStructs::_vs_low_offset = -1;
int VMStructs::_vs_high_offset = -1;
int VMStructs::_flag_type_offset = -1;
int VMStructs::_flag_name_offset = -1;
int VMStructs::_flag_addr_offset = -1;
const char *VMStructs::_flags_addr = NULL;
Expand Down Expand Up @@ -321,7 +322,9 @@ void VMStructs::initOffsets() {
_array_data_offset = *(int *)(entry + offset_offset);
}
} else if (strcmp(type, "JVMFlag") == 0 || strcmp(type, "Flag") == 0) {
if (strcmp(field, "_name") == 0 || strcmp(field, "name") == 0) {
if (strcmp(field, "_type") == 0 || strcmp(field, "type") == 0) {
_flag_type_offset = *(int *)(entry + offset_offset);
} else if (strcmp(field, "_name") == 0 || strcmp(field, "name") == 0) {
_flag_name_offset = *(int *)(entry + offset_offset);
} else if (strcmp(field, "_addr") == 0 || strcmp(field, "addr") == 0) {
_flag_addr_offset = *(int *)(entry + offset_offset);
Expand Down Expand Up @@ -781,6 +784,21 @@ void *JVMFlag::find(const char *name) {
return NULL;
}

void *JVMFlag::find(const char *name, int type_mask) {
if (_flags_addr != NULL && _flag_size > 0) {
for (int i = 0; i < _flag_count; i++) {
JVMFlag *f = (JVMFlag *)(_flags_addr + i * _flag_size);
if (f->name() != NULL && strcmp(f->name(), name) == 0) {
int masked = 0x1 << f->type();
if (masked & type_mask) {
return f->addr();
}
}
}
}
return NULL;
}

int NMethod::findScopeOffset(const void *pc) {
intptr_t pc_offset = (const char *)pc - code();
if (pc_offset < 0 || pc_offset > 0x7fffffff) {
Expand Down
3 changes: 3 additions & 0 deletions ddprof-lib/src/main/cpp/vmStructs.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class VMStructs {
static int _vs_high_bound_offset;
static int _vs_low_offset;
static int _vs_high_offset;
static int _flag_type_offset;
static int _flag_name_offset;
static int _flag_addr_offset;
static const char *_flags_addr;
Expand Down Expand Up @@ -528,8 +529,10 @@ class CodeHeap : VMStructs {
class JVMFlag : VMStructs {
public:
static void *find(const char *name);
static void *find(const char *name, int type_mask);

const char *name() { return *(const char **)at(_flag_name_offset); }
int type() { return *(int *)at(_flag_type_offset); }

void *addr() { return *(void **)at(_flag_addr_offset); }
};
Expand Down
157 changes: 157 additions & 0 deletions ddprof-lib/src/main/java/com/datadoghq/profiler/JVMAccess.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.datadoghq.profiler;

import java.util.function.Consumer;

/**
* An internal JVM access support.
* <p>
* We are using vmstructs and dynamic symbol lookups to provide access to some JVM internals.
* There will be dragons here. We are touching and possibly mutating JVM internals. Do not use
* unless you know what you are doing.
* </p>
*/
public final class JVMAccess {
private static final class SingletonHolder {
static final JVMAccess INSTANCE = new JVMAccess();
}

/**
* Flags interface to access JVM flags.
* In general, the flags are read-only. However, some flags can be modified at runtime.
* Currently, only string and boolean flags can be modified. Allowing modification of numeric

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would specify as-of details here

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand - what should I specify here?

* flags would require exact specification of the flag type (int, long, float, double) such
* that the correct number of bytes would be written to the flag and not overwrite the surrounding
* memory.
*/
public interface Flags {
Flags NONE = new Flags() {
@Override
public String getStringFlag(String name) {
return null;
}

@Override
public void setStringFlag(String name, String value) {
}

@Override
public boolean getBooleanFlag(String name) {
return false;
}

@Override
public void setBooleanFlag(String name, boolean value) {
}

@Override
public long getIntFlag(String name) {
return 0;
}

@Override
public double getFloatFlag(String name) {
return 0;
}
};

String getStringFlag(String name);
void setStringFlag(String name, String value);
boolean getBooleanFlag(String name);
void setBooleanFlag(String name, boolean value);
long getIntFlag(String name);
double getFloatFlag(String name);
}

private class FlagsImpl implements Flags {
public String getStringFlag(String name) {
return findStringJVMFlag0(name);
}

public void setStringFlag(String name, String value) {
setStringJVMFlag0(name, value);
}

public boolean getBooleanFlag(String name) {
Comment thread
jbachorik marked this conversation as resolved.
return findBooleanJVMFlag0(name);
}

public void setBooleanFlag(String name, boolean value) {
setBooleanJVMFlag0(name, value);
}

public long getIntFlag(String name) {
return findIntJVMFlag0(name);
}

public double getFloatFlag(String name) {
return findFloatJVMFlag0(name);
}
}

public static JVMAccess getInstance() {
return SingletonHolder.INSTANCE;
}

private final LibraryLoader.Result libraryLoadResult;
private final Flags flags;

private JVMAccess() {
LibraryLoader.Result result = LibraryLoader.builder().load();;
if (result.success) {
// library loaded successfully, check if we can access JVM
try {
healthCheck0();
} catch (Throwable t) {
// failed to access JVM; update the result
result = new LibraryLoader.Result(false, t);
}

}
if (!result.success && result.error != null) {
System.out.println("[WARNING] Failed to obtain JVM access.\n" + result.error);
Comment thread
MattAlp marked this conversation as resolved.
}
flags = result.success ? new FlagsImpl() : Flags.NONE;
libraryLoadResult = result;
}

public JVMAccess(String libLocation, String scratchDir, Consumer<Throwable> errorHandler) {
LibraryLoader.Result result = LibraryLoader.builder().withLibraryLocation(libLocation).withScratchDir(scratchDir).load();
if (result.success) {
// library loaded successfully, check if we can access JVM
try {
healthCheck0();
} catch (Throwable t) {
// failed to access JVM; update the result
result = new LibraryLoader.Result(false, t);
}

}
if (!result.success && result.error != null) {
if (errorHandler != null) {
errorHandler.accept(result.error);
} else {
System.out.println("[WARNING] Failed to obtain JVM access.\n" + result.error);
Comment thread
MattAlp marked this conversation as resolved.
}
}
flags = result.success ? new FlagsImpl() : Flags.NONE;
libraryLoadResult = result;
}

public Flags flags() {
return flags;
}

public boolean isActive() {
return libraryLoadResult.success;
}

// a dummy method to check if the library has loaded properly
private native boolean healthCheck0();

private native String findStringJVMFlag0(String name);
private native void setStringJVMFlag0(String name, String value);
private native boolean findBooleanJVMFlag0(String name);
private native void setBooleanJVMFlag0(String name, boolean value);
private native long findIntJVMFlag0(String name);
private native double findFloatJVMFlag0(String name);
}
Loading